perm filename PARSE.PAS[AL,HE]1 blob
sn#679399 filedate 1982-09-27 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00052 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00007 00002 (*$E+ Routines to parse an AL program into the internal format *)
C00008 00003 (* datatype definitions *)
C00011 00004 (* statement definitions *)
C00015 00005 (* auxiliary definitions: variable, etc. *)
C00017 00006 (* definition of the ubiquitous NODE record *)
C00023 00007 (* records for parser: ident, token, resword *)
C00027 00008 (* process descriptor blocks & environment record definitions *)
C00031 00009 (* global variables *)
C00033 00010 (* aux routines from/to elsewhere *)
C00036 00011 (* lookup functions: upperCase, eqStrng, hash, resLookup, idLookup, freeIds, enterIdent *)
C00041 00012 (* aux routines: makeNewVar, makeUVar & varLookup *)
C00046 00013 (* routine to make reserved words: initReswords *)
C00060 00014 (* routine to make predeclared identifiers & constants: initIdents *)
C00082 00015 (* parser initialization routine: initParser *)
C00085 00016 (* basic read routines: readLine & fileopen *)
C00095 00017 (* routine to show where error occurred: errprnt *)
C00097 00018 (* getToken *)
C00117 00019 (* aux routines: findResword & appendEnd *)
C00120 00020 (* aux routines for dimension checking: matchdim, getdim, checkdim *)
C00127 00021 (* aux routines for parsing expressions: getDelim, defNode, getDtype, checkarg, copyExpr, ppFlush *)
C00134 00022 (* aux routines for parsing expressions(cont): getargs *)
C00148 00023 (* function to parse expressions: exprParse *)
C00166 00024 (* auxiliary expression mungers: relExpr & evalOrder *)
C00174 00025 (* aux routines for parsing blocks: getDeclarations & checkBlkids *)
C00192 00026 function blockParse(st: statementp): boolean
C00199 00027 function coblockParse(st: statementp): boolean
C00203 00028 function endParse(st: statementp): boolean
C00205 00029 function assignParse(st: statementp): boolean
C00211 00030 function ifParse(st: statementp): boolean
C00214 00031 function forParse(st: statementp): boolean
C00220 00032 function whileParse(st: statementp): boolean
C00222 00033 function untilParse(st: statementp): boolean
C00224 00034 function caseParse(st: statementp): boolean
C00231 00035 function returnParse(st: statementp): boolean
C00234 00036 function affixParse(st: statementp): boolean
C00240 00037 function unfixParse(st: statementp): boolean
C00243 00038 function signalParse(st: statementp): boolean
C00245 00039 function pauseParse(st: statementp): boolean
C00246 00040 function printParse(st: statementp): boolean
C00247 00041 (* aux functions for motion clauses: thencode & clauseParse *)
C00264 00042 function cmonParse(st: statementp deferred: boolean): boolean
C00271 00043 function enableParse(st: statementp): boolean
C00273 00044 function moveParse(st: statementp): boolean
C00297 00045 function stopParse(st: statementp): boolean
C00299 00046 function retryParse(st: statementp): boolean
C00300 00047 function wristParse(st: statementp): boolean
C00302 00048 function requireParse(st: statementp): boolean
C00306 00049 function defineParse(st: statementp): boolean
C00312 00050 function dimensionParse(st: statementp): boolean
C00318 00051 function stmntParse (*: statementp *)
C00326 00052 (* program parser *)
C00329 ENDMK
C⊗;
(*$E+ Routines to parse an AL program into the internal format *)
(*$S3000 use a large codesize *)
program parse;
(* random type declarations for OMSI/SAIL compatibility *)
type
(* ascii = char; *)
atext = packed file of ascii;
(* atext = text; *)
vectorp = ↑vector;
transp = ↑trans;
strngp = ↑strng;
eventp = ↑event;
framep = ↑frame;
statementp = ↑statement;
varidefp = ↑varidef;
nodep = ↑node;
identp = ↑ident;
tokenp = ↑token;
reswordp = ↑resword;
pdbp = ↑pdb;
envheaderp = ↑envheader;
enventryp = ↑enventry;
environp = ↑environment;
cmoncbp = ↑cmoncb;
(* datatype definitions *)
datatypes = (pconstype, varitype, svaltype, vectype, rottype, transtype,
frametype, eventtype, strngtype, labeltype, proctype, arraytype,
reftype, valtype, cmontype, nulltype, undeftype,
dimensiontype, mactype, macargtype, freevartype);
scalar = real;
vector = record refcnt: integer; val: array [1..3] of real end;
trans = record refcnt: integer; val: array [1..3,1..4] of real end;
cstring = packed array [1..10] of ascii;
c4str = packed array [1..4] of ascii;
c5str = packed array [1..5] of ascii;
c20str = packed array [1..20] of ascii;
linestr = packed array [1..130] of ascii;
strng = record
next: strngp;
ch: cstring;
end;
event = record
next: eventp; (* all events are on one big list *)
count: integer;
waitlist: pdbp;
end;
frame = record
vari: varidefp; (* back pointer to variable name & info *)
calcs: nodep; (* affixment info *)
case ftype: boolean of (* frame = true, device = false *)
true: (valid: integer; val, fdepr: transp; dcntr: integer; dev: framep);
false: (mech: integer; case sdev: boolean of
true: (sdest: real); false: (tdest,appr,depr: transp));
(* sdev = true for scalar devices, false for frames *)
end;
byte = 0..255; (* doesn't really belong here, but... *)
(* statement definitions *)
stmntypes = (progtype, blocktype, coblocktype, endtype, coendtype,
fortype, iftype, whiletype, untiltype, casetype,
calltype, returntype,
printtype, prompttype, pausetype, aborttype, assigntype,
signaltype, waittype, enabletype, disabletype, cmtype,
affixtype, unfixtype,
movetype, operatetype, opentype, closetype, centertype,
stoptype, retrytype,
requiretype, definetype, macrotype, commenttype, dimdeftype,
setbasetype, wristtype, tovaltype, declaretype, emptytype);
(* more??? *)
statement = packed record
next, last: statementp; (* ↑ to lexical tokens? *)
stlab: varidefp;
exprs: nodep; (* any expressions used by this statement *)
nlines: integer;
bpt: boolean;
case stype: stmntypes of
progtype: (pcode: statementp; errors: integer);
blocktype,
declaretype,
endtype,
coendtype: (bcode, bparent: statementp; blkid: identp;
level, numvars: 0..255; variables: varidefp);
coblocktype: (threads: nodep; nthreads: integer; cblkid: identp);
fortype: (forvar, initial, step, final: nodep; fbody: statementp);
whiletype,
untiltype: (cond: nodep; body: statementp);
casetype: (index: nodep; range, ncases: integer; caselist: nodep);
iftype: (icond: nodep; thn, els: statementp);
pausetype: (ptime: nodep);
prompttype,
printtype,
aborttype: (plist: nodep; debugLev: integer);
returntype: (retval, rproc: nodep);
calltype,
assigntype: (what, aval: nodep);
affixtype,
unfixtype: (frame1, frame2, byvar, atexp: nodep; rigid: boolean);
signaltype,
waittype: (event: nodep);
movetype,
operatetype,
opentype,
closetype,
centertype,
stoptype: (cf, clauses: nodep);
retrytype: (rcode, rparent: statementp; olevel: integer);
cmtype: (oncond: nodep; conclusion: statementp;
deferCm, exprCm: boolean; cdef: varidefp);
enabletype,
disabletype: (cmonlab: varidefp);
requiretype: (rfil: boolean; rfils: strngp; rfilen: integer);
definetype: (macname,mpars: varidefp; macdef: tokenp);
commenttype: (len: integer; str: strngp; cbody: statementp);
dimdeftype: (dimname: varidefp; dimexpr: nodep);
setbasetype,
wristtype: (fvec, tvec: nodep);
tovaltype: (vstr: strngp; vlen: integer; waitp: boolean);
end;
(* auxiliary definitions: variable, etc. *)
varidef = packed record
next,dnext: varidefp;
name: identp;
level: 0..255; (* environment level *)
offset: 0..255; (* environment offset *)
dtype: varidefp; (* to hold the dimension info *)
tbits: 0..15; (* special type bits: array = 1, proc = 2, ref = 4 & ? *)
dbits: 0..15; (* for use by debugger/interpreter *)
case vtype: datatypes of
arraytype: (a: nodep);
proctype: (p: nodep);
labeltype,
cmontype: (s: statementp);
mactype: (mdef: statementp);
macargtype: (marg: tokenp);
pconstype: (c: nodep);
dimensiontype: (dim: nodep);
end;
(* definition of the ubiquitous NODE record *)
nodetypes = (exprnode, leafnode, listnode, clistnode, colistnode, forvalnode,
deprnode, viaptnode, apprnode, destnode, durnode,
sfacnode, wobblenode, swtnode, nullingnode, wristnode, cwnode,
arrivalnode, departingnode,
ffnode, forcenode, stiffnode, gathernode, cmonnode, errornode,
calcnode, arraydefnode, bnddefnode, bndvalnode,
waitlistnode, procdefnode, tlistnode, dimnode, commentnode);
exprtypes = ( svalop, (* scalar operators *)
sltop, sleop, seqop, sgeop, sgtop, sneop, (* relations *)
notop, orop, xorop, andop, eqvop, (* logical *)
saddop, ssubop, smulop, sdivop, snegop, sabsop, (* scalar ops *)
sexpop, maxop, minop, intop, idivop, modop,
sqrtop, logop, expop, timeop, (* functions *)
sinop, cosop, tanop, asinop, acosop, atan2op, (* trig *)
vdotop, vmagnop, tmagnop,
vecop, (* vector operators *)
vmakeop, unitvop, vaddop, vsubop, crossvop, vnegop,
svmulop, vsmulop, vsdivop, tvmulop, wrtop,
tposop, taxisop,
transop, (* trans operators *)
tmakeop, torientop, ttmulop, tvaddop, tvsubop, tinvrtop,
vsaxwrop, constrop, ftofop, deproachop, fmakeop, vmkfrcop,
ioop, (* i/o operators *)
queryop, inscalarop,
specop, (* special operators *)
arefop, callop, grinchop, macroop, vmop, adcop, dacop,
badop,
addop, subop, negop, mulop, divop, absop); (* for parsing *)
leaftypes = pconstype..strngtype;
reltypes = sltop..sgtop;
forcetypes = (force,absforce,torque,abstorque,angvelocity);
node = record
next: nodep;
case ntype: nodetypes of
exprnode: (op: exprtypes; arg1, arg2, arg3: nodep; elength: integer);
leafnode: (case ltype: leaftypes of
varitype: (vari: varidefp; vid: identp);
pconstype: (cname: varidefp; pcval: nodep);
svaltype: (s: scalar; wid: integer);
vectype: (v: vectorp);
transtype: (t: transp);
strngtype: (length: integer; str: strngp) ); (* also used by commentnodes *)
listnode: (lval: nodep);
clistnode: (cval: integer; stmnt: statementp; clast: nodep);
colistnode: (prev: nodep; cstmnt: statementp);
forvalnode: (fvar: enventryp; fstep: scalar);
arrivalnode:(evar: varidefp);
deprnode,
apprnode,
destnode: (loc: nodep; code: statementp);
viaptnode: (vlist: boolean; via,duration,velocity: nodep; vcode: statementp);
durnode: (durrel: reltypes; durval: nodep);
sfacnode,
wobblenode,
swtnode: (clval: nodep);
nullingnode,
wristnode,
cwnode: (notp: boolean); (* true = nonulling/zero wrist/counter_clockwise *)
ffnode: (ff: nodep; csys, pdef: boolean); (* true = world, false = hand *)
forcenode: (ftype: forcetypes; frel: reltypes; fval, fvec, fframe: nodep);
stiffnode: (fv, mv, coc: nodep);
gathernode: (gbits: integer);
cmonnode: (cmon: statementp; errhandlerp: boolean);
errornode: (eexpr: nodep);
calcnode: (rigid, frame1: boolean; other: framep; case tvarp: boolean of
false: (tval: transp); true: (tvar: enventryp) );
arraydefnode: (numdims: 1..10; bounds: nodep; combnds: boolean);
bnddefnode: (lower, upper: nodep);
bndvalnode: (lb, ub, mult: integer);
waitlistnode: (who: pdbp; when: integer);
procdefnode:(ptype: datatypes; level: 0..255;
pname, paramlist: varidefp; body: statementp);
tlistnode: (tok: tokenp);
dimnode: (time, distance, angle, dforce: integer);
end;
(* records for parser: ident, token, resword *)
ident = record
next: identp;
length: integer;
name: strngp;
predefined: varidefp;
end;
tokentypes = (reswdtype, identtype, constype, comnttype, delimtype, labeldeftype,
macpartype);
constypes = svaltype..strngtype;
reswdtypes = (stmnttype, filtype, clsetype, decltype, optype, edittype);
filtypes = (abouttype,alongtype,attype,bytype,defertype,dotype,elsetype,
errmodestype,fromtype,handtype,intype,nonrigidlytype,rigidlytype,
sourcefiletype,steptype,thentype,totype,untltype,viatype,
withtype,worldtype,zeroedtype,oftype,wheretype,nowaittype,
ontype,offtype,ppsizetype,collecttype,alltype,lextype);
clsetypes = (approachtype,arrivaltype,departuretype,departingtype,durationtype,
errortype,forcetype,forceframetype,forcewristtype,gathertype,
nildeproachtype,nonullingtype,nullingtype,stiffnesstype,
torquetype,velocitytype,wobbletype,
cwtype,ccwtype,stopwaittimetype,angularvelocitytype,
fxtype,fytype,fztype,mxtype,mytype,mztype,
t1type,t2type,t3type,t4type,t5type,t6type,tbltype);
edittypes = (getcmd,savecmd,insertcmd,renamecmd,startcmd,gocmd,proceedcmd,
stepcmd,sstepcmd,nstepcmd,gstepcmd,executecmd,setcmd,tracecmd,
breakcmd,unbreakcmd,tbreakcmd,definecmd,markcmd,unmarkcmd,
popcmd);
token = record
next: tokenp;
case ttype: tokentypes of
constype: (cons: nodep);
comnttype: (len: integer; str: strngp);
delimtype: (ch: ascii);
reswdtype: (case rtype: reswdtypes of
stmnttype: (stmnt: stmntypes);
filtype: (filler: filtypes);
clsetype: (clause: clsetypes);
decltype: (decl: datatypes);
optype: (op: exprtypes);
edittype: (ed: edittypes) );
identtype: (id: identp);
labeldeftype: (lab: varidefp);
macpartype: (mpar: varidefp);
end;
resword = record
next: reswordp;
length: integer;
name: strngp;
case rtype: reswdtypes of
stmnttype: (stmnt: stmntypes);
filtype: (filler: filtypes);
clsetype: (clause: clsetypes);
decltype: (decl: datatypes);
optype: (op: exprtypes);
edittype: (ed: edittypes);
end;
(* process descriptor blocks & environment record definitions *)
queuetypes = (nullqueue,nowrunning,runqueue,inputqueue,eventqueue,sleepqueue,
proccall);
pdb = packed record
nextpdb,next: pdbp; (* for list of all/active pdb's *)
level: 0..255; (* lexical level *)
mode: 0..255; (* expression/statement/sub-statement *)
priority: 0..255; (* probably never greater than 3? *)
status: queuetypes; (* what are we doing *)
env: envheaderp;
spc: statementp; (* current statement *)
epc: nodep; (* current expression (if any) *)
sp: nodep; (* intermediate value stack *)
cm: cmoncbp; (* if we're a cmon point to our definition *)
mech: framep; (* current device being used *)
linenum: integer; (* used by editor/debugger *)
case procp: boolean of (* true if we're a procedure *)
true: (opdb: pdbp; (* pdb to restore when procedure exits *)
pdef: nodep); (* procedure definition node *)
false: (evt: eventp; (* event to signal when process goes away *)
sdef: statementp); (* first statement where process was defined *)
end;
envheader = packed record
parent: envheaderp;
env: array [0..4] of environp;
varcnt: 0..255; (* # of variables in use ??? *)
case procp: boolean of (* true if we're a procedure *)
true: (proc: nodep);
false:(block: statementp);
end;
enventry = record
case etype: datatypes of
svaltype: (s: scalar);
vectype: (v: vectorp);
transtype: (t: transp);
frametype: (f: framep);
eventtype: (evt: eventp);
strngtype: (length: integer; str: strngp);
cmontype: (c: cmoncbp);
proctype: (p: nodep; penv: envheaderp);
reftype: (r: enventryp);
arraytype: (a: envheaderp; bnds: nodep);
end;
environment = record
next: environp;
vals: array [0..9] of enventryp;
end;
cmoncb = record
running, enabled: boolean; (* cmon's status *)
cmon: statementp;
pdb: pdbp;
evt: eventp;
fbits: integer; (* bits for force sensing *)
oldcmon: cmoncbp; (* for debugger *)
end;
(* global variables *)
var reswords: array [0..26] of reswordp;
idents: array [0..26] of identp;
macrostack: array [1..10] of tokenp;
curmacstack: array [1..10] of varidefp;
macrodepth: integer;
backup, semiseen, shownline, expandmacros, flushcomments: boolean;
curtoken: token;
(* filestack: array [1..5] of atext; *)
file1,file2,file3,file4,file5: atext;
filedepth: integer;
line: linestr;
curchar, maxchar, curline, curpage: integer;
sysVars,unVars: varidefp;
eofError: boolean;
errcount: integer;
curBlock,outerBlock,newDeclarations: statementp;
curVariable: varidefp;
curProc: varidefp;
curMotion: statementp;
inMove,inCoblock: boolean;
endOk,coendOk: integer;
moveLevel: integer;
curErrhandler, curCmon: statementp;
pnode: nodep;
nodim, distancedim, timedim, angledim,
forcedim, torquedim, veldim, angveldim: varidefp;
fvstiffdim, mvstiffdim: nodep;
dimCheck: boolean;
(* various constant pointers *)
xhat,yhat,zhat,nilvect: vectorp;
niltrans: transp;
bpark, ypark, gpark, rpark: transp; (* arm park positions *)
(* aux routines from/to elsewhere *)
function getsysVars: varidefp;
begin getsysVars := sysVars; end;
function newToken: tokenp; extern; (* from ALLOC.PAS *)
procedure relToken(t: tokenp); extern;
function newNode: nodep; extern;
procedure relNode(n: nodep); extern;
function newStrng: strngp; extern;
procedure relStrng(n: strngp); extern;
function newVector: vectorp; extern;
procedure relVector(n: vectorp); extern;
function newTrans: transp; extern;
procedure relTrans(n: transp); extern;
function newIdent: identp; extern;
procedure relIdent(n: identp); extern;
function newVaridef: varidefp; extern;
procedure relVaridef(n: varidefp); extern;
function newStatement: statementp; extern;
procedure relStatement(n: statementp); extern;
(* from INTERP.PAS *)
procedure passConstants(var x,y,z,nv: vectorp; var b,yp,g,r,nt: transp); extern;
procedure ppLine; extern; (* from EDIT.PAS *)
procedure ppOutNow; extern;
procedure ppChar(ch: ascii); extern;
procedure pp5(ch: c5str; length: integer); extern;
procedure pp10(ch: cstring; length: integer); extern;
procedure pp10L(ch: cstring; length: integer);extern;
procedure pp20(ch: c20str; length: integer); extern;
procedure pp20L(ch: c20str; length: integer); extern;
procedure ppInt(i: integer); extern;
procedure ppDtype(d: datatypes); extern;
procedure ppStrng(length: integer; s: strngp); extern;
function eReadLine(var line: linestr): integer; extern;
function eCopyLine(var line: linestr): integer; extern;
procedure freeStatement(s: statementp); extern; (* from FREE.PAS *)
procedure freStrng(st: strngp); extern;
(* lookup functions: upperCase, eqStrng, hash, resLookup, idLookup, freeIds, enterIdent *)
function upperCase(c: ascii): ascii;
begin
if (c < chr(141B)) or (chr(172B) < c) then upperCase := c
else upperCase := chr(ord(c) - 40B); (* c - 'a' + 'A' *)
end;
function eqStrng(s1: strngp; s2,len: integer): boolean;
var i,j: integer; b: boolean; c1,c2: ascii;
begin
b := true;
i := 0;
j := 1;
repeat
c1 := upperCase(s1↑.ch[j]);
c2 := upperCase(line[s2+i]);
if c1 <> c2 then b := false
else
begin
i := i + 1;
if j < 10 then j := j + 1
else begin j := 1; s1 := s1↑.next end;
end
until (i >= len) or not b;
eqStrng := b;
end;
function hash(ch: ascii): integer;
var i: integer;
begin (* this will only work for ascii *)
i := ord(ch);
if ('A' <= ch) and (ch <= 'Z') then i := i - ord('A') + 1
else if (chr(141B) <= ch) and (ch <= chr(172B)) then i := i - 141B + 1
else i := 0;
hash := i;
end;
function resLookup(str,len: integer): reswordp;
var res: reswordp; b: boolean;
begin
res := reswords[hash(line[str])]; (* look in right bucket *)
b := true;
while (res <> nil) and b do
if res↑.length = len then
if eqStrng(res↑.name,str,len) then b := false
else res := res↑.next
else res := res↑.next;
resLookup := res;
end;
function idLookup(str,len: integer): identp;
var id: identp; b: boolean;
begin
id := idents[hash(line[str])]; (* look in right bucket *)
b := true;
while (id <> nil) and b do
if id↑.length = len then
if eqStrng(id↑.name,str,len) then b := false
else id := id↑.next
else id := id↑.next;
idLookup := id;
end;
procedure freeIds;
var i: integer; id,idp,idn: identp; st,stp: strngp;
begin
for i := 1 to 26 do
begin
idp := nil;
id := idents[i];
while id <> nil do
with id↑ do
begin
idn := next;
if predefined = nil then
begin (* flush id now *)
st := name; (* done with string *)
while st <> nil do
begin stp := st↑.next; relStrng(st); st := stp end;
relIdent(id); (* and ident *)
end
else
begin
if idp = nil then idents[i] := id else idp↑.next := id;
idp := id;
end;
id := idn;
end;
if idp = nil then idents[i] := nil;
end;
end;
function getReswords(ch: ascii): reswordp;
begin
getReswords := reswords[hash(ch)]; (* pass back right bucket *)
end;
function getIdents(ch: ascii): identp;
begin
getIdents := idents[hash(ch)]; (* pass back right bucket *)
end;
procedure enterIdent(id: identp); (* used by EDIT *)
var i: integer;
begin
i := hash(id↑.name↑.ch[1]); (* find proper bucket *)
id↑.next := idents[i]; (* link us onto list of identifiers *)
idents[i] := id;
end;
(* aux routines: makeNewVar, makeUVar & varLookup *)
function makeNewVar(vartype: datatypes; vid: identp): varidefp;
var v: varidefp;
begin
v := newVaridef;
with v↑ do
begin
vtype := vartype;
dtype := nil;
name := vid;
next := nil;
tbits := 0;
dnext := nil;
dbits := 0;
s := nil;
if curBlock <> nil then level := curBlock↑.level else level := 0;
if curVariable <> nil then
begin
offset := curVariable↑.offset + 1;
curVariable↑.next := v; (* add var to current block's list of vars *)
end
else
begin
offset := 0;
if curBlock <> nil then curBlock↑.variables := v;
end;
end;
curVariable := v;
makeNewVar := v;
end;
function makeUVar(vartype: datatypes; vid: identp): varidefp;
var v,oldCurVariable: varidefp; sp,oldCurBlock: statementp;
begin
oldCurVariable := curVariable;
oldCurBlock := curBlock;
curBlock := outerBlock; (* assume outermost block *)
v := curProc; (* unless in body of an enclosing procedure *)
while v <> nil do
begin
sp := oldCurBlock;
while sp <> nil do
if v↑.p↑.level + 1 = sp↑.level then
begin curBlock := sp; v := nil; sp := nil end
else if v↑.p↑.level >= sp↑.level then sp := nil else sp := sp↑.bparent;
if v <> nil then v := v↑.dnext;
end;
curVariable := curBlock↑.variables;
if curVariable <> nil then (* find last defined variable *)
while curVariable↑.next <> nil do curVariable := curVariable↑.next;
v := makeNewVar(vartype,vid);
sp := newStatement; (* add a new declaration statement to start of block *)
with sp↑ do
begin
stype := declaretype; variables := v; numvars := 1;
last := curBlock; next := curBlock↑.bcode;
end;
if newDeclarations = nil then newDeclarations := sp; (* for edit *)
with curBlock↑ do
begin (* splice us into block *)
if bcode <> nil then bcode↑.last := sp;
bcode := sp;
end;
curBlock := oldCurBlock;
curVariable := oldCurVariable;
makeUVar := v;
end;
function varLookup(id: identp): varidefp;
var v,vp: varidefp; st: statementp; b,bp: boolean;
begin
st := curBlock;
vp := curProc;
bp := vp <> nil;
b := true;
while (st <> nil) and b do
begin
if bp then
if vp↑.level = st↑.level then
begin (* check procedures parameter's *)
v := vp↑.p↑.paramlist;
vp := vp↑.dnext; (* hack - up pointer to nesting proc defs *)
bp := vp <> nil;
end
else
begin (* use block vars *)
v := st↑.variables;
st := st↑.bparent;
end
else (* if dumb Pascal had short-circuit AND's this would be cleaner... *)
begin (* use block vars *)
v := st↑.variables;
st := st↑.bparent;
end;
while (v <> nil) and b do
if v↑.name = id then b := false else v := v↑.next;
end;
if b then v := id↑.predefined; (* maybe it's a predefined variable? *)
varLookup := v;
end;
(* routine to make reserved words: initReswords *)
procedure initParser; (* body starts in 2 pages *)
var i: integer;
procedure initReswords;
var i: integer; res: reswordp; Estr: strngp;
function makeResword(t: reswdtypes; s: cstring): reswordp;
var res: reswordp; str: strngp; i,len: integer;
begin
new(res);
with res↑ do
begin
rtype := t;
str := newStrng;
str↑.ch := s;
name := str;
len := 10;
while s[len] = ' ' do len := len - 1;
length := len;
end;
i := hash(s[1]); (* find proper bucket *)
res↑.next := reswords[i]; (* link us onto list of reserved words *)
reswords[i] := res;
makeResword := res;
end;
procedure stmake(st: stmntypes; s: cstring);
var res: reswordp;
begin
res := makeResword(stmnttype,s);
res↑.stmnt := st;
end;
procedure filmake(fil: filtypes; s: cstring);
var res: reswordp;
begin
res := makeResword(filtype,s);
res↑.filler := fil;
end;
procedure clmake(cl: clsetypes; s: cstring);
var res: reswordp;
begin
res := makeResword(clsetype,s);
res↑.clause := cl;
end;
procedure dcmake(dc: datatypes; s: cstring);
var res: reswordp;
begin
res := makeResword(decltype,s);
res↑.decl := dc;
end;
procedure opmake(opr: exprtypes; s: cstring);
var res: reswordp;
begin
res := makeResword(optype,s);
res↑.op := opr;
end;
procedure editmake(ed: edittypes; s: cstring);
var res: reswordp;
begin
res := makeResword(edittype,s);
res↑.ed := ed;
end;
begin
for i := 0 to 26 do reswords[i] := nil;
stmake(progtype,'PROGRAM ');
stmake(blocktype,'BEGIN ');
stmake(coblocktype,'COBEGIN ');
stmake(coendtype,'COEND ');
stmake(endtype,'END ');
stmake(assigntype,':= ');
stmake(fortype,'FOR ');
stmake(iftype,'IF ');
stmake(whiletype,'WHILE ');
stmake(casetype,'CASE ');
stmake(returntype,'RETURN ');
stmake(printtype,'PRINT ');
stmake(prompttype,'PROMPT ');
stmake(pausetype,'PAUSE ');
stmake(aborttype,'ABORT ');
stmake(signaltype,'SIGNAL ');
stmake(waittype,'WAIT ');
stmake(enabletype,'ENABLE ');
stmake(disabletype,'DISABLE ');
stmake(cmtype,'ON ');
stmake(affixtype,'AFFIX ');
stmake(unfixtype,'UNFIX ');
stmake(movetype,'MOVE ');
stmake(operatetype,'OPERATE ');
stmake(opentype,'OPEN ');
stmake(closetype,'CLOSE ');
stmake(centertype,'CENTER ');
stmake(stoptype,'STOP ');
stmake(retrytype,'RETRY ');
stmake(requiretype,'REQUIRE ');
stmake(definetype,'DEFINE ');
stmake(dimdeftype,'DIMENSION ');
stmake(commenttype,'COMMENT ');
stmake(setbasetype,'SETBASE ');
stmake(wristtype,'WRIST ');
stmake(tovaltype,'VAL ');
filmake(abouttype,'ABOUT ');
filmake(alongtype,'ALONG ');
filmake(attype,'AT ');
filmake(bytype,'BY ');
filmake(defertype,'DEFER ');
filmake(dotype,'DO ');
filmake(elsetype,'ELSE ');
res := makeResword(filtype,'ERROR_MODE');
res↑.name↑.next := newStrng;
res↑.name↑.next↑.ch := 'S ';
res↑.length := 11;
res↑.filler := errmodestype;
filmake(fromtype,'FROM ');
filmake(handtype,'HAND ');
filmake(intype,'IN ');
filmake(nonrigidlytype,'NONRIGIDLY');
filmake(rigidlytype,'RIGIDLY ');
res := makeResword(filtype,'SOURCE_FIL');
Estr := newStrng;
Estr↑.ch := 'E ';
res↑.name↑.next := Estr;
res↑.length := 11;
res↑.filler := sourcefiletype;
filmake(steptype,'STEP ');
filmake(thentype,'THEN ');
filmake(totype,'TO ');
filmake(untltype,'UNTIL ');
filmake(viatype,'VIA ');
filmake(withtype,'WITH ');
filmake(worldtype,'WORLD ');
filmake(zeroedtype,'ZEROED ');
filmake(oftype,'OF ');
filmake(wheretype,'WHERE ');
filmake(nowaittype,'NOWAIT ');
clmake(approachtype,'APPROACH ');
clmake(arrivaltype,'ARRIVAL ');
clmake(departuretype,'DEPARTURE ');
clmake(departingtype,'DEPARTING ');
clmake(durationtype,'DURATION ');
clmake(errortype,'ERROR ');
clmake(forcetype,'FORCE ');
res := makeResword(clsetype,'FORCE_FRAM');
res↑.name↑.next := Estr;
res↑.length := 11;
res↑.clause := forceframetype;
res := makeResword(clsetype,'FORCE_WRIS');
res↑.name↑.next := newStrng;
res↑.name↑.next↑.ch := 'T ';
res↑.length := 11;
res↑.clause := forcewristtype;
clmake(gathertype,'GATHER ');
clmake(fxtype,'FX ');
clmake(fytype,'FY ');
clmake(fztype,'FZ ');
clmake(mxtype,'MX ');
clmake(mytype,'MY ');
clmake(mztype,'MZ ');
clmake(t1type,'T1 ');
clmake(t2type,'T2 ');
clmake(t3type,'T3 ');
clmake(t4type,'T4 ');
clmake(t5type,'T5 ');
clmake(t6type,'T6 ');
clmake(tbltype,'TBL ');
res := makeResword(clsetype,'NILDEPROAC');
res↑.name↑.next := newStrng;
res↑.name↑.next↑.ch := 'H ';
res↑.length := 11;
res↑.clause := nildeproachtype;
clmake(nonullingtype,'NO_NULLING');
clmake(nullingtype,'NULLING ');
clmake(stiffnesstype,'STIFFNESS ');
clmake(torquetype,'TORQUE ');
clmake(velocitytype,'VELOCITY ');
clmake(wobbletype,'WOBBLE ');
clmake(cwtype,'CW ');
clmake(cwtype,'CLOCKWISE ');
clmake(ccwtype,'CCW ');
res := makeResword(clsetype,'COUNTER_CL');
res↑.name↑.next := newStrng;
res↑.name↑.next↑.ch := 'OCKWISE ';
res↑.length := 17;
res↑.clause := ccwtype;
res := makeResword(clsetype,'ANGULAR_VE');
res↑.name↑.next := newStrng;
res↑.name↑.next↑.ch := 'LOCITY ';
res↑.length := 16;
res↑.clause := angularvelocitytype;
res := makeResword(clsetype,'STOP_WAIT_');
res↑.name↑.next := newStrng;
res↑.name↑.next↑.ch := 'TIME ';
res↑.length := 14;
res↑.clause := stopwaittimetype;
dcmake(arraytype,'ARRAY ');
dcmake(eventtype,'EVENT ');
dcmake(labeltype,'LABEL ');
dcmake(proctype,'PROCEDURE ');
dcmake(reftype,'REFERENCE ');
dcmake(svaltype,'SCALAR ');
dcmake(valtype,'VALUE ');
opmake(sltop,'< ');
opmake(sleop,'<= ');
opmake(sleop,'=< ');
opmake(seqop,'= ');
opmake(sgeop,'>= ');
opmake(sgeop,'=> ');
opmake(sgtop,'> ');
opmake(sneop,'<> ');
opmake(notop,'NOT ');
opmake(orop,'OR ');
opmake(xorop,'XOR ');
opmake(andop,'AND ');
opmake(eqvop,'EQV ');
opmake(sexpop,'↑ ');
opmake(maxop,'MAX ');
opmake(minop,'MIN ');
opmake(intop,'INT ');
opmake(idivop,'DIV ');
opmake(modop,'MOD ');
opmake(sqrtop,'SQRT ');
opmake(logop,'LOG ');
opmake(expop,'EXP ');
opmake(timeop,'RUNTIME ');
opmake(sinop,'SIN ');
opmake(cosop,'COS ');
opmake(tanop,'TAN ');
opmake(asinop,'ASIN ');
opmake(acosop,'ACOS ');
opmake(atan2op,'ATAN2 ');
opmake(vdotop,'. ');
opmake(unitvop,'UNIT ');
opmake(vmakeop,'VECTOR ');
opmake(wrtop,'WRT ');
opmake(tposop,'POS ');
opmake(taxisop,'AXIS ');
opmake(tmakeop,'TRANS ');
opmake(fmakeop,'FRAME ');
opmake(torientop,'ORIENT ');
opmake(tinvrtop,'INV ');
opmake(vsaxwrop,'ROT ');
opmake(constrop,'CONSTRUCT ');
opmake(deproachop,'DEPROACH ');
opmake(ftofop,'-> ');
opmake(queryop,'QUERY ');
opmake(inscalarop,'INSCALAR ');
opmake(adcop,'ADC ');
opmake(dacop,'DAC ');
opmake(addop,'+ ');
opmake(subop,'- ');
opmake(mulop,'* ');
opmake(divop,'/ ');
(* opmake(absop,'| '); since dumb SAIL doesn't handle the | char *)
res := makeResword(optype,'| ');
res↑.op := absop;
res↑.name↑.ch[1] := chr(174B);
opmake(grinchop,'# ');
editmake(getcmd,'GET '); (* for use by the editor/debugger *)
editmake(savecmd,'SAVE ');
editmake(insertcmd,'INSERT ');
editmake(renamecmd,'RENAME ');
editmake(startcmd,'START ');
editmake(startcmd,'RUN ');
editmake(gocmd,'GO ');
editmake(proceedcmd,'PROCEED ');
editmake(sstepcmd,'SSTEP ');
editmake(nstepcmd,'NSTEP ');
editmake(gstepcmd,'GSTEP ');
editmake(executecmd,'EXECUTE ');
editmake(setcmd,'SET ');
editmake(tracecmd,'TRACE ');
editmake(breakcmd,'BREAK ');
editmake(unbreakcmd,'UNBREAK ');
editmake(tbreakcmd,'TBREAK ');
editmake(markcmd,'MARK ');
editmake(unmarkcmd,'UNMARK ');
editmake(popcmd,'POP ');
filmake(offtype,'OFF ');
filmake(ppsizetype,'BOTSIZE ');
filmake(collecttype,'COLLECT ');
filmake(alltype,'ALL ');
filmake(lextype,'LEX ');
end;
(* routine to make predeclared identifiers & constants: initIdents *)
procedure initIdents;
var i: integer; id: identp; v,vp: varidefp; n: nodep; str,Rstr: strngp;
sfId,degId,secId: identp; t,tp: tokenp; (* for macro defs *)
function makeIdent(s: cstring): identp;
var id: identp; str: strngp; i,len: integer;
begin
id := newIdent;
with id↑ do
begin
predefined := nil;
str := newStrng;
str↑.ch := s;
name := str;
len := 10;
while s[len] = ' ' do len := len - 1;
length := len;
end;
i := hash(id↑.name↑.ch[1]); (* find proper bucket *)
id↑.next := idents[i]; (* link us onto list of identifiers *)
idents[i] := id;
makeIdent := id;
end;
function DimMake(s: cstring): varidefp;
var id: identp; vdef: varidefp; n: nodep;
begin
id := makeIdent(s);
vdef := newVaridef;
id↑.predefined := vdef;
n := newNode; (* need to make up a dimension node *)
with n↑ do
begin
next := nil;
ntype := dimnode;
time := 0;
distance := 0;
angle := 0;
dforce := 0;
end;
with vdef↑ do
begin
name := id;
vtype := dimensiontype;
dtype := vdef; (* a bit circular, but... *)
offset := 0;
tbits := 0;
dbits := 0;
dim := n;
dnext := nil;
end;
DimMake := vdef;
end;
function Idmake(s: cstring; d: datatypes; vdim: varidefp; o: integer): identp;
var id: identp; vdef: varidefp;
begin
id := makeIdent(s);
vdef := newVaridef;
id↑.predefined := vdef;
with vdef↑ do
begin
name := id;
vtype := d;
dtype := vdim;
level := 0;
offset := o;
tbits := 0;
dbits := 0;
next := sysVars;
dnext := nil;
end;
sysVars := vdef; (* add us to list of system variables *)
Idmake := id;
end;
function ConMake(s: cstring; d: datatypes; vdim: varidefp;
sv: real; n: nodep): identp;
var id: identp; vdef: varidefp;
begin
id := makeIdent(s);
vdef := newVaridef;
id↑.predefined := vdef;
if n = nil then (* need to make up a new constant node *)
begin
n := newNode;
with n↑ do
begin
next := nil;
ntype := leafnode;
ltype := d;
if d = svaltype then s := sv;
end;
end;
with vdef↑ do
begin
name := id;
vtype := pconstype;
dtype := vdim;
offset := 0;
tbits := 0;
dbits := 0;
c := n;
dnext := nil;
end;
ConMake := id;
end;
function MacMake(s: cstring): identp;
var id: identp; vdef: varidefp;
begin
id := makeIdent(s);
vdef := newVaridef;
id↑.predefined := vdef;
vdef↑.name := id;
vdef↑.vtype := macargtype;
MacMake := id;
end;
function CToken(num: real; tp: tokenp): tokenp;
var t: tokenp; n: nodep;
begin
t := newToken;
if tp <> nil then tp↑.next := t;
n := newNode;
t↑.ttype := constype;
t↑.cons := n;
n↑.ntype := leafnode;
n↑.ltype := svaltype;
n↑.s := num;
CToken := t;
end;
function IToken(i: identp; tp: tokenp): tokenp;
var t: tokenp;
begin
t := newToken;
if tp <> nil then tp↑.next := t;
t↑.ttype := identtype;
t↑.id := i;
IToken := t;
end;
function RToken(r: reswdtypes): tokenp;
var t: tokenp;
begin
t := newToken;
t↑.ttype := reswdtype;
t↑.rtype := r;
RToken := t;
end;
function WithToken(tp: tokenp): tokenp;
var t: tokenp;
begin
t := RToken(filtype);
if tp <> nil then tp↑.next := t;
t↑.filler := withtype;
WithToken := t;
end;
function OpToken(tp: tokenp): tokenp;
var t: tokenp;
begin
t := RToken(optype);
if tp <> nil then tp↑.next := t;
t↑.op := seqop;
OpToken := t;
end;
function ClToken(cl: clsetypes; tp: tokenp): tokenp;
var t: tokenp;
begin
t := RToken(clsetype);
if tp <> nil then tp↑.next := t;
t↑.clause := cl;
ClToken := t;
end;
function FilToken(fil: filtypes; tp: tokenp): tokenp;
var t: tokenp;
begin
t := RToken(filtype);
if tp <> nil then tp↑.next := t;
t↑.filler := fil;
FilToken := t;
end;
procedure SpdSt(id: identp; spd: real);
var t,tp: tokenp;
begin
t := IToken(sfId,nil);
id↑.predefined↑.marg := t;
tp := RToken(stmnttype);
t↑.next := tp;
tp↑.stmnt := assigntype;
t := CToken(spd,tp);
t↑.next := nil;
end;
procedure SpdCl(id: identp; spd: real);
var t,tp: tokenp;
begin
t := WithToken(nil);
id↑.predefined↑.marg := t;
tp := IToken(sfId,t);
t := OpToken(tp);
tp := CToken(spd,t);
tp↑.next := nil;
end;
procedure SwtCl(id: identp; swt: real);
var t,tp: tokenp;
begin
t := WithToken(nil);
id↑.predefined↑.marg := t;
tp := ClToken(stopwaittimetype,t);
t := OpToken(tp);
tp := CToken(swt,t);
tp↑.next := nil;
end;
begin
for i := 0 to 26 do idents[i] := nil;
nodim := DimMake('DIMENSIONL'); (* define basic dimension types *)
nodim↑.name↑.name↑.next := newStrng;
nodim↑.name↑.name↑.next↑.ch := 'ESS ';
nodim↑.name↑.length := 13;
angledim := DimMake('ANGLE ');
angledim↑.dim↑.angle := 64; (* really 1, but use 64 so sqrt has a chance *)
distancedim := DimMake('DISTANCE ');
distancedim↑.dim↑.distance := 64;
timedim := DimMake('TIME ');
timedim↑.dim↑.time := 64;
forcedim := DimMake('FORCE ');
forcedim↑.dim↑.dforce := 64;
torquedim := DimMake('TORQUE ');
torquedim↑.dim↑.dforce := 64; (* torque = distance * force *)
torquedim↑.dim↑.distance := 64;
veldim := DimMake('VELOCITY ');
veldim↑.dim↑.time := -64; (* velocity = distance / time *)
veldim↑.dim↑.distance := 64;
angveldim := DimMake('ANGULAR_VE');
angveldim↑.name↑.name↑.next := newStrng;
angveldim↑.name↑.name↑.next↑.ch := 'LOCITY ';
angveldim↑.name↑.length := 16;
angveldim↑.dim↑.time := -64; (* angular_velocity = angle / time *)
angveldim↑.dim↑.angle := 64;
fvstiffdim := newNode; (* stiffness fv = force / distance *)
with fvstiffdim↑ do
begin
next := nil;
ntype := dimnode;
time := 0;
distance := -64;
angle := 0;
dforce := 64;
end;
mvstiffdim := newNode; (* stiffness mv = torque / angle *)
with mvstiffdim↑ do
begin
next := nil;
ntype := dimnode;
time := 0;
distance := 64;
angle := -64;
dforce := 64;
end;
sysVars := nil; (* declare all the system variables *)
id := Idmake('BARM ',frametype,distancedim,0);
id := Idmake('BARM_ERROR',svaltype,nodim,1);
id := Idmake('BHAND ',svaltype,distancedim,2);
id := Idmake('BHAND_ERRO',svaltype,nodim,3);
Rstr := newStrng;
Rstr↑.ch := 'R ';
id↑.name↑.next := Rstr;
id↑.length := 11;
id := Idmake('YARM ',frametype,distancedim,4);
id := Idmake('YARM_ERROR',svaltype,nodim,5);
id := Idmake('YHAND ',svaltype,distancedim,6);
id := Idmake('YHAND_ERRO',svaltype,nodim,7);
id↑.name↑.next := Rstr;
id↑.length := 11;
id := Idmake('GARM ',frametype,distancedim,8);
id := Idmake('GARM_ERROR',svaltype,nodim,9);
id := Idmake('GHAND ',svaltype,distancedim,10);
id := Idmake('GHAND_ERRO',svaltype,nodim,11);
id↑.name↑.next := Rstr;
id↑.length := 11;
id := Idmake('RARM ',frametype,distancedim,12);
id := Idmake('RARM_ERROR',svaltype,nodim,13);
id := Idmake('RHAND ',svaltype,distancedim,14);
id := Idmake('RHAND_ERRO',svaltype,nodim,15);
id↑.name↑.next := Rstr;
id↑.length := 11;
id := Idmake('DRIVER ',svaltype,nodim,16); (* same as DRIVER_TURNS *)
id := Idmake('DRIVER_TUR',svaltype,nodim,16); (* same as DRIVER *)
id↑.name↑.next := newStrng;
id↑.name↑.next↑.ch := 'NS ';
id↑.length := 12;
sysVars := sysVars↑.next; (* don't want both in list of sysVars *)
id := Idmake('DRIVER_ERR',svaltype,nodim,17);
id↑.name↑.next := newStrng;
id↑.name↑.next↑.ch := 'OR ';
id↑.length := 12;
id := Idmake('DRIVER_TIP',frametype,distancedim,18);
id := Idmake('DRIVER_GRA',frametype,distancedim,19);
id↑.name↑.next := newStrng;
id↑.name↑.next↑.ch := 'SP ';
id↑.length := 12;
id := Idmake('VISE ',svaltype,distancedim,20);
id := Idmake('VISE_ERROR',svaltype,nodim,21);
id := Idmake('FIXED_JAW ',frametype,distancedim,22);
id := Idmake('MOVING_JAW',frametype,distancedim,23);
sfId := Idmake('SPEED_FACT',svaltype,nodim,24);
sfid↑.name↑.next := newStrng;
sfid↑.name↑.next↑.ch := 'OR ';
sfId↑.length := 12;
v := sysVars; (* reverse the list so it's in the right order *)
while v <> nil do
begin
vp := v↑.next;
if vp <> nil then vp↑.dnext := v (* set up a back pointer for next step *)
else sysVars := v;
v↑.next := v↑.dnext; (* use back pointer to reverse list *)
v↑.dnext := nil;
v := vp;
end;
(* now make up the constants *)
id := ConMake('BPARK ',transtype,distancedim,0.0,nil);
id↑.predefined↑.c↑.t := bpark;
id := ConMake('YPARK ',transtype,distancedim,0.0,nil);
id↑.predefined↑.c↑.t := ypark;
id := ConMake('RPARK ',transtype,distancedim,0.0,nil);
id↑.predefined↑.c↑.t := rpark;
id := ConMake('GPARK ',transtype,distancedim,0.0,nil);
id↑.predefined↑.c↑.t := gpark;
id := ConMake('NILTRANS ',transtype,distancedim,0.0,nil);
n := id↑.predefined↑.c;
n↑.t := niltrans;
id := ConMake('NILROT ',transtype,angledim,0.0,n);
id := ConMake('STATION ',transtype,distancedim,0.0,n);
id := ConMake('XHAT ',vectype,nodim,0.0,nil);
id↑.predefined↑.c↑.v := xhat;
id := ConMake('YHAT ',vectype,nodim,0.0,nil);
id↑.predefined↑.c↑.v := yhat;
id := ConMake('ZHAT ',vectype,nodim,0.0,nil);
id↑.predefined↑.c↑.v := zhat;
id := ConMake('NILVECT ',vectype,nodim,0.0,nil);
id↑.predefined↑.c↑.v := nilvect;
id := ConMake('TRUE ',svaltype,nodim,1.0,nil);
n := id↑.predefined↑.c;
degId := ConMake('DEG ',svaltype,angledim,0.0,n);
id := ConMake('DEGREES ',svaltype,angledim,0.0,n);
id := ConMake('INCH ',svaltype,distancedim,0.0,n);
id := ConMake('INCHES ',svaltype,distancedim,0.0,n);
id := ConMake('OUNCES ',svaltype,forcedim,0.0,n);
id := ConMake('OZ ',svaltype,forcedim,0.0,n);
secId := ConMake('SEC ',svaltype,timedim,0.0,n);
id := ConMake('SECOND ',svaltype,timedim,0.0,n);
id := ConMake('SECONDS ',svaltype,timedim,0.0,n);
id := ConMake('FALSE ',svaltype,nodim,0.0,nil);
id := ConMake('CM ',svaltype,distancedim,0.3937008,nil);
id := ConMake('GM ',svaltype,forcedim,0.035274,nil);
id := ConMake('RADIANS ',svaltype,angledim,57.295779,nil);
id := ConMake('PI ',svaltype,nodim,3.1415927,nil);
id := ConMake('LBS ',svaltype,forcedim,16.0,nil);
id := ConMake('RPM ',svaltype,angveldim,6.0,nil);
id := ConMake('CRLF ',strngtype,nodim,0.0,nil);
str := newStrng;
str↑.ch[1] := chr(15B); (* cr *)
str↑.ch[2] := chr(12B); (* lf *)
id↑.predefined↑.c↑.str := str;
id↑.predefined↑.c↑.length := 2;
id := ConMake('PANIC_BUTT',svaltype,nodim,1024.0,nil); (* '2000 *)
id↑.name↑.next := newStrng;
id↑.name↑.next↑.ch := 'ON ';
id↑.length := 12;
id := ConMake('EXCESSIVE_',svaltype,nodim,2048.0,nil); (* '4000 *)
id↑.name↑.next := newStrng;
id↑.name↑.next↑.ch := 'FORCE ';
id↑.length := 15;
id := ConMake('TIME_OUT ',svaltype,nodim,4096.0,nil); (* '10000 *)
id := MacMake('DIRECTLY '); (* now make predeclared macros *)
t := WithToken(nil); (* "WITH APPROACH = NILDEPROACH" *)
id↑.predefined↑.marg := t;
tp := ClToken(approachtype,t);
t := OpToken(tp);
tp := ClToken(nildeproachtype,t);
t := WithToken(tp); (* "WITH DEPARTURE = NILDEPROACH" *)
tp := ClToken(departuretype,t);
t := OpToken(tp);
tp := ClToken(nildeproachtype,t);
tp↑.next := nil;
SpdSt(MacMake('QUICK '),1.0); (* QUICK = "SPEEDFACTOR := 1.0" *)
SpdSt(MacMake('SLOW '),3.0); (* SLOW = "SPEEDFACTOR := 3.0" *)
SpdSt(MacMake('CAUTIOUS '),4.0); (* CAUTIOUS = "SPEEDFACTOR := 4.0" *)
SpdCl(MacMake('QUICKLY '),1.0); (* QUICKLY = "WITH SPEEDFACTOR = 1.0" *)
SpdCl(MacMake('NORMALLY '),2.0); (* NORMALLY = "WITH SPEEDFACTOR = 2.0" *)
SpdCl(MacMake('SLOWLY '),3.0); (* SLOWLY = "WITH SPEEDFACTOR = 3.0" *)
SpdCl(MacMake('CAUTIOUSLY'),4.0); (* CAUTIOUSLY = "WITH SPEEDFACTOR = 4.0" *)
id := MacMake('APPROXIMAT');
id↑.name↑.next := newStrng;
id↑.name↑.next↑.ch := 'ELY ';
id↑.length := 13;
t := WithToken(nil); (* APPROXIMATELY = "WITH NONULLING" *)
id↑.predefined↑.marg := t;
tp := ClToken(nonullingtype,t);
tp↑.next := nil;
id := MacMake('PRECISELY ');
t := WithToken(nil); (* PRECISELY = "WITH NULLING" *)
id↑.predefined↑.marg := t;
tp := ClToken(nullingtype,t);
tp↑.next := nil;
SwtCl(MacMake('GENTLY '),0.0); (* GENTLY = "WITH STOPWAITTIME = 0.0" *)
SwtCl(MacMake('TIGHTLY '),0.5); (* TIGHTLY = "WITH STOPWAITTIME = 0.5" *)
id := MacMake('TIL ');
t := filToken(steptype,nil); (* TIL = "STEP 1 UNTIL" *)
id↑.predefined↑.marg := t;
tp := CToken(1.0,t);
t := filToken(untltype,tp);
t↑.next := nil;
end;
(* parser initialization routine: initParser *)
begin
macrodepth := 0;
expandmacros := true;
filedepth := 0; (* use tty for input *)
curchar := 1;
maxchar := -1;
curline := 0;
curpage := 1;
sysVars := nil;
unVars := nil;
eofError := false;
backup := false;
curToken.next := nil;
curBlock := nil;
outerBlock := nil;
curVariable := nil;
curProc := nil;
curMotion := nil;
curCmon := nil;
curErrhandler := nil;
newDeclarations := nil;
flushcomments := true;
inCoblock := false;
endOk := 0;
coendOk := 0;
dimCheck := true;
initReswords;
passConstants(xhat,yhat,zhat,nilvect,bpark,ypark,gpark,rpark,niltrans);
initIdents;
pnode := newNode;
with pnode↑ do
begin (* used to get print lists for print, prompt & abort statements *)
ntype := exprnode;
op := queryop;
end;
end;
procedure parpntrs(var n,d,t,a,f,tor,v,av: varidefp; var fv,mv,p: nodep;
var nt: transp; var x,y,z: vectorp);
begin (* to pass back pointers to predefined dimensions *)
n := nodim;
d := distancedim;
t := timedim;
a := angledim;
f := forcedim;
tor := torquedim;
v := veldim;
av := angveldim;
fv := fvstiffdim;
mv := mvstiffdim;
p := pnode;
nt := niltrans;
x := xhat;
y := yhat;
z := zhat;
end;
(* basic read routines: readLine & fileopen *)
procedure readline;
var i: integer;
procedure rdLine(var fi: atext);
var ch: ascii; i,j: integer;
procedure addit(c: c4str);
var i: integer;
begin
if c[1] = ' ' then
begin
for i := 1 to 4 do line[maxchar+i-1] := c[i];
ch := ' ';
maxchar := maxchar + 4;
end
else
begin
line[maxchar] := c[1];
ch := c[2];
maxchar := maxchar + 1;
end;
end;
begin
maxchar := 0;
if eofError or eof(fi) then
begin
if filedepth >= 1 then
begin (* continue with last file *)
filedepth := filedepth - 1;(* pop up a level *)
ppLine; (* give luser a sense of progress *)
readline; (* try again with popped file *)
end
else
begin (* yow - no file left - complain *)
pp20L('*** End of File enco',20); pp20L('untered while parsin',20);
pp10('g program ',10); ppLine;
eofError := true;
line[1] := 'E'; (* force parser to give up *)
line[2] := 'N';
line[3] := 'D';
line[4] := ';';
line[5] := ' ';
curchar := 1;
maxchar := 5;
end
end
else
begin (* normal case - read in next line *)
(* for SAIL we have to use the following to get full ASCII character set *)
if ord(fi↑) = 15B then get(fi);
while not eof(fi) and not (ord(fi↑)=15B) and (maxchar < 129) do
begin
ch := fi↑;
if not ((ord(ch) = 12B) or (ord(ch) = 0)) then (* ignore linefeeds & nulls *)
begin
maxchar := maxchar + 1;
case ord(ch) of (* so we can use some of the extra characters on SAIL *)
137B: addit(':= '); (* "←" → ":=" *)
034B: addit('<= '); (* "≤" → "<=" *)
035B: addit('>= '); (* "≥" → ">=" *)
033B: addit('<> '); (* "≠" → "<>" *)
031B: addit('-> '); (* "→" → "->" *)
004B: addit(' and'); (* "∧" → " and " *)
005B: addit(' not'); (* "¬" → " not " *)
037B: addit(' or '); (* "∨" → " or " *)
036B: addit(' eqv'); (* "≡" → " eqv " *)
026B: ch := '#'; (* "⊗" → "#" *)
007B: addit(' pi '); (* "π" → " pi " *)
020B, (* "⊂" → "\" so we can read old AL macro delimiters *)
021B: ch := '\'; (* "⊃" → "\" *)
030B: ch := '_'; (* "_" → "_" because Pascal on SAIL's so dumb *)
end;
if ord(ch) <> 11B then line[maxchar] := ch
else
begin (* turn tabs into spaces *)
i := 8*(((maxchar - 1) div 8) + 1);
for j := maxchar to i do line[j] := ' ';
maxchar := i;
end;
end;
get(fi);
end;
(* for OMSI we can just use the following:
if eoln(fi) then readln(fi);
while not eoln(fi) and (maxchar < 129) do
begin
maxchar := maxchar + 1;
read(fi,line[maxchar]);
if ord(line[maxchar]) = 11B then (* turn tabs into spaces *)
(* begin
i := 8*(((maxchar - 1) div 8) + 1);
for j := maxchar to i do line[j] := ' ';
maxchar := i;
end;
end; *)
line[maxchar+1] := ' '; (* always can count on a final blank *)
if line[1] <> chr(14B) then begin curchar := 1; curline := curline + 1; end
else (* new page *)
begin
curpage := curpage + 1;
ppInt(curpage); (* give luser a sense of progress *)
ppChar(' ');
ppOutNow;
curline := 1;
curchar := 2;
line[1] := ' ';
end;
end;
end;
begin
case filedepth of
0: begin
maxChar := eReadLine(line); (* get the line from edit *)
curchar := 1;
end;
1: rdline(file1);
2: rdline(file2);
3: rdline(file3);
4: rdline(file4);
5: rdline(file5);
end;
shownline := false;
end;
procedure errprnt; forward;
procedure fileOpen(len: integer; strp: strngp);
var ip,i,j,k,prj,prg: integer; ch: char; fname: packed array [1..9] of char;
b: boolean; str: strngp;
procedure sixbit(ch: ascii; var ppn: integer);
begin
if ppn < 10000B then ppn := ppn * 100B + (ord(ch) - ord(' '))
else begin pp10L('Bad ppn ',7); errprnt; end;
end;
function nextchar: char;
begin
if i < len then
begin
if j < 10 then j := j + 1 else begin j := 1; str := str↑.next end;
nextchar := upperCase(str↑.ch[j]);
end
else nextchar := ' ';
i := i + 1;
end;
begin
str := strp;
ip := 1;
prj := 0;
prg := 0;
i := 0;
j := 0;
ch := nextchar;
while (ch <> '.') and (ch <> '[') and (ch <> ' ') and (i <= len) do
begin (* parse file name *)
if ip <= 6 then begin fname[ip] := ch; ip := ip + 1 end
else begin pp20L('Bad file name ',13); errprnt; end;
ch := nextchar;
end;
for k := ip to 6 do fname[k] := ' ';
ip := 7;
if ch = '.' then (* parse file extension *)
begin
ch := nextchar;
while (ch <> '[') and (ch <> ' ') and (i <= len) do
begin
if ip <= 9 then begin fname[ip] := ch; ip := ip + 1 end
else begin pp20L('Bad file extension ',18); errprnt; end;
ch := nextchar;
end;
end;
for k := ip to 9 do fname[k] := ' ';
if ch = '[' then (* parse ppn *)
begin
ch := nextchar; (* skip over '[' *)
while (ch <> ',') and (i <= len) do
begin
sixbit(ch,prj);
ch := nextchar;
end;
if prj >= 400000B then prj := (prj - 400000B) * 1000000B + 400000000000B
else prj := prj * 1000000B;
ch := nextchar; (* skip over comma *)
while (ch <> ']') and (i <= len) do
begin
sixbit(ch,prg);
ch := nextchar;
end;
end;
k := prj + prg;
case filedepth of
1: begin reset(file1,fname,0,k); b := eof(file1); end;
2: begin reset(file2,fname,0,k); b := eof(file2); end;
3: begin reset(file3,fname,0,k); b := eof(file3); end;
4: begin reset(file4,fname,0,k); b := eof(file4); end;
5: begin reset(file5,fname,0,k); b := eof(file5); end;
end;
if b then
begin (* means file wasn't found - complain *)
filedepth := filedepth - 1;
pp20L('File not found: ',16); ppStrng(len,strp); ppLine;
end;
end;
(* routine to show where error occurred: errprnt *)
procedure errprnt;
var i,j: integer; s: strngp;
begin
errcount := errcount + 1; (* keep track of how many errors we've reported *)
if (not shownline) and ((filedepth > 0) or (macrodepth > 0)) then
begin (* tell where error occured *)
ppLine; ppChar('p'); ppInt(curpage); pp5(', l ',3); ppInt(curline);
if macrodepth > 0 then
begin
pp20(' while expanding mac',20); pp5('ro: ',4);
with curmacstack[macrodepth]↑.name↑ do
begin
s := name;
j := 1;
for i := 1 to length do
begin
ppChar(s↑.ch[j]);
if j < 10 then j := j + 1 else begin j := 1; s := s↑.next; end
end;
end;
end;
ppLine;
(* if reading a file then ..... *)
for i := 1 to maxchar do ppChar(line[i]); (* show line *)
shownline := true;
end;
ppLine;
for i := 1 to curchar-1 do ppChar(' '); (* show where in line *)
ppChar('↑'); ppLine;
end;
(* getToken *)
function copyExpr(n: nodep; lcp: boolean): nodep; forward;
function copyToken: tokenp; (* aux function used by getToken & elsewhere *)
var t: tokenp;
begin
t := newToken; (* get a new token *)
with curToken do (* copy the token's fields from curToken *)
begin
t↑.next := next;
t↑.ttype := ttype;
if ttype = constype then t↑.cons := copyExpr(cons,true)
else
begin
t↑.rtype := rtype;
t↑.len := len; (* this should work ... *)
t↑.str := str;
end;
end;
copyToken := t;
end;
procedure getToken;
var b,bp: boolean; v,vp: varidefp; t,tp: tokenp; n: nodep;
i,j,l: integer; r,rf: real;
ch,chp: ascii; res: reswordp; id: identp; st: strngp;
procedure addChar(ch: ascii; var s: strngp; var j: integer);
begin
if j < 10 then j := j + 1
else begin j := 1; s↑.next := newStrng; s := s↑.next; s↑.next := nil end;
s↑.ch[j] := ch;
end;
procedure upToken(t: tokenp);
begin
if t <> nil then
with t↑ do (* copy the token's fields into curToken *)
begin
curToken.next := next;
curToken.ttype := ttype;
if ttype = constype then curToken.cons := copyExpr(cons,true)
else
begin
curToken.rtype := rtype;
curToken.len := len; (* this should work ... *)
curToken.str := str;
end;
end;
end;
begin
if backup then backup := false (* use current token *)
else if macrodepth > 0 then
begin (* get next token in macro *)
if curToken.next = nil then
begin (* end of current macro - pop up a level *)
v := curmacstack[macrodepth]; (* definition for current macro *)
if v <> nil then
if v↑.vtype = mactype then v := v↑.mdef↑.mpars (* args for macro *)
else v := nil; (* no args *)
while v <> nil do (* need to release old tokens *)
begin
t := v↑.marg;
while t <> nil do begin tp := t↑.next; relToken(t); t := tp end;
v := v↑.next;
end;
curToken.next := macrostack[macrodepth]; (* pop old token *)
macrodepth := macrodepth - 1;
getToken; (* try again *)
end
else upToken(curToken.next); (* otherwise just copy the next token *)
end
else
begin (* scan line for next token *)
if curchar > maxchar then readline;
while (line[curchar] = ' ') or (line[curchar] = chr(11B)) do (* skip blanks *)
if curchar < maxchar then curchar := curchar + 1 else readline;
ch := line[curchar]; (* first char of next token *)
if (('A' <= ch) and (ch <= 'Z')) or (ch = chr(137B)) or (* A..Z,_ *)
((chr(141B) <= ch) and (ch <= chr(172B))) then (* a..z *)
begin (* identifier or reserved word *)
l := curchar;
repeat
l := l + 1;
ch := line[l];
until not ((('0' <= ch) and (ch <= '9')) or (('A' <= ch) and (ch <= 'Z'))
or ((chr(141B) <= ch) and (ch <= chr(172B))) or (ch = chr(137B)));
l := l - curchar; (* length of string *)
res := resLookup(curchar,l);
if res <> nil then
begin
with curToken do (* it's a reserved word *)
begin
ttype := reswdtype;
rtype := res↑.rtype;
stmnt := res↑.stmnt; (* copy whatever type it really is *)
end; (* all fields are same length here *)
if (res↑.rtype = stmnttype) and (res↑.stmnt = commenttype) then
begin (* read comment *)
if not flushcomments then
begin
curToken.ttype := comnttype;
st := newStrng;
st↑.next := nil;
curToken.str := st;
j := 0;
l := 0;
end;
repeat
ch := line[curchar];
if not flushcomments then
begin
addChar(ch,st,j);
l := l + 1;
end;
if (curchar < maxchar) or (ch = ';') then curchar := curchar + 1
else
begin
readLine;
if not flushcomments then
begin
addChar(chr(15B),st,j); (* append a crlf *)
addChar(chr(12B),st,j);
l := l + 2;
end;
end;
until eofError or (ch = ';');
curToken.len := l;
if eofError then
begin
pp20L('*** while searching',20); pp20(' for end of comment ',19);
ppLine;
end
else if flushcomments then getToken; (* return a real token *)
end
else curchar := curchar + l;
end
else
begin
curToken.ttype := identtype; (* it's an identifier then *)
id := idLookup(curchar,l); (* see if it's already been defined *)
if id = nil then (* need to define it *)
begin
id := newIdent;
st := newStrng;
st↑.next := nil;
with id↑ do
begin
name := st;
length := l;
predefined := nil;
i := hash(line[curchar]); (* find proper bucket *)
next := idents[i]; (* link us onto list of identifiers *)
idents[i] := id;
end;
j := 0; (* now make a copy of the identifier's name *)
for i := curchar to curchar + l - 1 do addChar(line[i],st,j);
for i := j + 1 to 10 do st↑.ch[i] := ' '; (* for completeness... *)
end;
curchar := curchar + l;
if (line[curchar] <> ':') or (line[curchar+1] = '=') then
curToken.id := id (* we'll worry if it's a variable or constant later *)
else
begin (* looks like it's a label *)
curchar := curchar + 1; (* skip over the ':' *)
v := varLookup(id);
if v = nil then
begin (* undeclared label - be nice *)
pp20L('Undeclared identifie',20); pp20('r defined to be a la',20);
pp5('bel ',3);
errprnt;
v := makeUVar(labeltype,id);
v↑.s := nil;
end
else if v↑.vtype <> labeltype then
begin (* same name as existing variable *)
pp20L('Previously defined v',20); pp20('ariable used as labe',20);
pp10('l name ',6);
errprnt;
end
else if v↑.s <> nil then (* multiply defined label *)
begin
pp20L('Multiply defined lab',20); pp5('el ',2);
errprnt;
end;
if (v↑.vtype = labeltype) and (v↑.s = nil) then
begin (* it's a good label *)
curToken.ttype := labeldeftype;
curToken.lab := v;
end
else getToken; (* bad - ignore it & get a good token *)
end
end
end
else if (('0' <= ch) and (ch <= '9')) (* number *)
or ((ch='.') and ('0'<=line[curchar+1])and(line[curchar+1]<='9')) then
begin
r := 0;
while ('0' <= ch) and (ch <= '9') do
begin
r := 10 * r + (ord(ch) - ord('0'));
curchar := curchar + 1;
ch := line[curchar];
end;
if ch = '.' then (* read in fraction part *)
begin
curchar := curchar + 1; (* skip over '.' *)
ch := line[curchar];
rf := 1;
while ('0' <= ch) and (ch <= '9') do
begin
rf := rf * 10.0;
r := r + (ord(ch) - ord('0')) / rf;
curchar := curchar + 1;
ch := line[curchar];
end;
end;
n := newNode;
n↑.ntype := leafnode;
n↑.ltype := svaltype;
n↑.s := r;
curToken.ttype := constype;
curToken.cons := n;
end
else if ch = '"' then (* string *)
begin
st := newStrng;
st↑.next := nil;
n := newNode;
n↑.ntype := leafnode;
n↑.ltype := strngtype;
n↑.str := st;
curToken.ttype := constype;
curToken.cons := n;
l := 0;
j := 0;
repeat
if curchar < maxchar then curchar := curchar + 1
else
begin
readLine;
addChar(chr(15B),st,j); (* append a crlf *)
addChar(chr(12B),st,j);
l := l + 2;
end;
ch := line[curchar];
b := (ch = '"');
if b and (curchar < maxchar) then
if line[curchar+1] = '"' then
begin curchar := curchar + 1; b := false end;
if not b then
begin
addChar(line[curchar],st,j);
l := l + 1;
end;
until eofError or b;
if eofError then
begin
pp20L('*** while searching',20); pp20(' for end of string ',18);
ppLine;
end;
n↑.length := l;
curchar := curchar + 1;
st↑.next := nil;
end
else if (ch = chr(173B)) or (* chr(173B) = '{' *)
(((ch = '(') or (ch = '/')) and (line[curchar+1] = '*')) then
begin (* it's a comment *)
if not flushcomments then
begin
curToken.ttype := comnttype;
st := newStrng;
st↑.next := nil;
curToken.str := st;
j := 0;
end;
l := 0;
repeat
ch := line[curchar];
if not flushcomments then
begin
addChar(ch,st,j);
l := l + 1;
end;
b := ch=chr(176B); (* for SAIL right brace = 176B *)
if ((ch=')') or (ch='/')) and (1 < curchar) then b := line[curchar-1]='*';
if (curchar < maxchar) or b then curchar := curchar + 1
else
begin
readLine;
if not flushcomments then
begin
addChar(chr(15B),st,j); (* append a crlf *)
addChar(chr(12B),st,j);
l := l + 2;
end;
end;
until eofError or b;
curToken.len := l;
if eofError then
begin
pp20L('*** while searching',20); pp20(' for end of comment ',19);
ppLine;
end
else if flushcomments then getToken; (* return a real token *)
end
else (* delimiter or operator *)
begin
chp := line[curchar+1];
if ((ch = ':') and (chp = '=')) or (* := *)
((ch = '-') and (chp = '>')) or (* -> *)
(((ch = '<') or (ch = '>')) and (chp = '=')) or (* <= >= *)
((ch = '=') and ((chp = '<') or (chp = '>'))) or (* =< => *)
((ch = '<') and (chp = '>')) then l := 2 (* <> *)
else l := 1;
res := resLookup(curchar,l);
with curToken do
if res <> nil then (* it's an operator *)
begin
ttype := reswdtype;
rtype := res↑.rtype;
op := res↑.op;
end
else (* it's a delimiter *)
begin
ttype := delimtype;
ch := line[curchar];
end;
curchar := curchar + l;
end;
end;
b := expandmacros;
while b and ((curToken.ttype = identtype) or (curToken.ttype = macpartype)) do
begin (* see if we need to expand a macro *)
with curToken do
if ttype = identtype then v := varLookup(id) else v := mpar;
if v = nil then b := false
else if v↑.vtype = macargtype then
begin
macrodepth := macrodepth + 1;
macrostack[macrodepth] := curToken.next; (* push current token *)
curmacstack[macrodepth] := v; (* no arguments here *)
upToken(v↑.marg); (* actual macro arg *)
end
else if v↑.vtype = mactype then
begin
vp := v↑.mdef↑.mpars; (* get parameter list *)
if vp <> nil then (* bind macro parameters *)
begin
getToken; (* look for opening '(' *)
if (curToken.ttype <> delimtype) or (curToken.ch <> '(') then
begin (* didn't find opening '(' *)
backup := true;
pp20L('*** Macro arguments ',20); pp20('missing opening "(" ',20);
pp20('- good luck! ',12);
errprnt;
end;
while vp <> nil do
begin
getToken; (* see if it's a simple or \...\ arg *)
if (curToken.ttype = delimtype) and (curToken.ch = '\') then
begin
t := nil;
repeat
getToken; (* scan the argument *)
bp := (curToken.ttype = delimtype) and (curToken.ch = '\');
if not bp then
if t = nil then begin t := copyToken; tp := t end
else begin tp↑.next := copyToken; tp := tp↑.next end;
until bp;
end
else t := copyToken;
vp↑.marg := t;
vp := vp↑.next;
getToken; (* now get separating ',' or closing ')' *)
if vp <> nil then (* look for separating comma *)
if (curToken.ttype <> delimtype) or (curToken.ch <> ',') then
begin
backup := true;
pp20L('*** Macro args not s',20); pp20('eparated by "," - go',20);
pp10('od luck! ',8);
errprnt;
end;
end;
if (curToken.ttype <> delimtype) or (curToken.ch <> ')') then
begin
backup := true; (* back up so we'll reparse last token *)
pp20L('*** Macro arguments ',20); pp20('missing closing ")" ',20);
pp20('- good luck! ',12);
errprnt;
end;
end;
macrodepth := macrodepth + 1;
macrostack[macrodepth] := curToken.next; (* push current token *)
curmacstack[macrodepth] := v; (* save pointer to macro name *)
upToken(v↑.mdef↑.macdef); (* expand macro *)
end
else b := false;
end;
end;
(* aux routines: findResword & appendEnd *)
function findResword(what: reswdtypes; which, where: integer): reswordp;
var b: boolean; i: integer; r: reswordp;
begin
b := true;
i := where;
while b and (i<=26) do
begin (* try to find reserved word & print it out *)
r := reswords[i]; (* try next bucket *)
while b and (r <> nil) do
with r↑ do
if (what=rtype) and (which = ord(stmnt)) then b := false else r := next;
i := i + 1;
end;
findResword := r;
end;
procedure appendEnd(s,so: statementp);
var st: statementp;
begin
if so <> nil then
begin
st := newStatement;
so↑.next := st;
with st↑ do
begin
last := so;
blkid := nil;
stype := endtype;
bparent := s;
end;
end;
end;
(* aux routines for dimension checking: matchdim, getdim, checkdim *)
function stmntParse: statementp; forward;
function exprParse: nodep; forward;
function matchdim(d1,d2: nodep; exactp: boolean): boolean;
var b: boolean;
begin
with d1↑ do
b := (time = d2↑.time) and (distance = d2↑.distance) and
(angle = d2↑.angle) and (dforce = d2↑.dforce);
if not (b or exactp) then
begin (* see if we can coerce d1 or d2, i.e. one is dimensionless *)
with d1↑ do
if (time = 0) and (distance = 0) and (angle = 0) and (dforce = 0) then
b := true;
if not b then (* see if d2 is dimensionless *)
with d2↑ do
if (time = 0) and (distance = 0) and (angle = 0) and (dforce = 0) then
b := true;
end;
matchdim := b;
end;
function getdim(n: nodep; var d: nodep): nodep;
var vdim: varidefp; d1: nodep;
procedure dimCopy(dp: nodep);
begin
with d↑ do
begin
time := dp↑.time;
distance := dp↑.distance;
angle := dp↑.angle;
dforce := dp↑.dforce;
end
end;
procedure dimMod(d1,d2: nodep; i: real);
begin
with d↑ do
begin
time := d1↑.time + round(i * d2↑.time);
distance := d1↑.distance + round(i * d2↑.distance);
angle := d1↑.angle + round(i * d2↑.angle);
dforce := d1↑.dforce + round(i * d2↑.dforce);
end
end;
begin (* getdim *)
if d = nil then
begin
d := newNode; (* need to make up a new dimension node to hold result *)
d↑.ntype := dimnode;
end;
if n = nil then dimCopy(nodim↑.dim)
else
with n↑ do
if (ntype = leafnode) or (ntype = procdefnode) then
begin
if ntype = procdefnode then vdim := pname
else if ltype = varitype then vdim := vari
else if ltype = pconstype then vdim := cname
else vdim := nil;
if vdim <> nil then (* see if there's an associated dimension *)
with vdim↑ do
if dtype <> nil then vdim := dtype (* yes - use it *)
else
if (vtype = transtype) or (vtype = frametype) then vdim := distancedim
else if vtype = rottype then vdim := angledim else vdim := nil;
if vdim <> nil then dimCopy(vdim↑.dim) else dimCopy(nodim↑.dim)
end
else (* see what type of expression it is *)
begin
d1 := nil;
if (op <= eqvop) or ((sinop <= op) and (op <= tanop)) or (op = sexpop) or
(op = logop) or (op = expop) or (op = unitvop) or (op = taxisop) or
(op = queryop) or (op = inscalarop) or (op = adcop) or (op = vmop) then
dimCopy(nodim↑.dim)
else if op = timeop then dimCopy(timedim↑.dim)
else if ((asinop <= op) and (op <= atan2op)) or (op = torientop) or
(op = vsaxwrop) then dimCopy(angledim↑.dim)
else if (op = constrop) or (op = fmakeop) or (op = deproachop) or
(op = grinchop) then dimCopy(distancedim↑.dim)
else if (op = tmakeop) or (op = tvmulop) or (op = ttmulop) then
d := getdim(arg2,d)
else if (op = smulop) or (op = svmulop) or (op = vsmulop) or
(op = vdotop) or (op = crossvop) then
dimMod(getdim(arg1,d),getdim(arg2,d1),1.0)
else if (op = sdivop) or (op = idivop) or (op = vsdivop) then
dimMod(getdim(arg1,d),getdim(arg2,d1),-1.0)
else if (op = sqrtop) then dimMod(nodim↑.dim,getdim(arg1,d),0.5)
else if (op = negop) then dimMod(nodim↑.dim,getdim(arg1,d),-1.0)
(* special - used by dimension statement *)
else d := getdim(arg1,d); (* sadd,ssub,sneg,sabs,max,min,int,mod,vmagn,
tmagn,vmake,vadd,vsub,vneg,tpos,tvadd,tvsub,
tinvrt,ftof,aref,call,bad *)
if d1 <> nil then relNode(d1);
end;
getdim := d;
end;
procedure checkdim(n,d: nodep); (* expr n should be of dimension d *)
var dp: nodep;
begin
dp := nil;
if not matchdim(getdim(n,dp),d,dimCheck) then (* does dimension match ok? *)
begin
pp20L('Dimensions don''t mat',20); pp5('ch ',2);
errprnt;
end;
relNode(dp);
end;
(* aux routines for parsing expressions: getDelim, defNode, getDtype, checkarg, copyExpr, ppFlush *)
procedure getDelim(char: ascii);
begin
with curToken do
begin
getToken; (* now look for the char *)
if (ttype <> delimtype) or (ch <> char) then
begin
backup := true;
pp10L('Need a " ',8); ppChar(char); pp10('" here ',6);
errprnt;
end;
end;
end;
function defNode(d: datatypes): nodep;
var n: nodep;
begin
n := newNode;
with n↑ do
begin
ntype := lecvnode;
ltype := d;
case d of
svaltype: s := 0.0;
vectype: v := nilvect;
rottype,
transtype: t := niltrans;
end;
end;
defNode := n;
end;
function getDtype(n: nodep): datatypes;
var da: datatypes;
begin
with n↑ do
if ntype = leafnode then
if ltype = varitype then da := vari↑.vtype
else if ltype = pconstype then da := pcval↑.ltype
else da := ltype
else (* see what type of op we've got *)
if (svalop < op) and (op < vecop) or
(ioop < op) and (op < specop) then da := svaltype else
if (vecop < op) and (op < transop) then da := vectype else
if (transop < op) and (op < ioop) then da := transtype else
if (op = arefop) or (op = callop) then da := arg1↑.vari↑.vtype else
if (op = grinchop) then da := getDtype(arg1) else
if (op = vmop) or (op = adcop) then da := svaltype else
if (op = badop) then da := getDtype(arg2) else da := nulltype;
getDtype := da;
end;
function checkArg(n: nodep; d: datatypes): nodep;
var bad: nodep; da: datatypes;
begin
if n = nil then checkArg := defNode(d) (* use default value *)
else
begin
da := getdtype(n);
if (da <> d) and ((da = frametype) or (da = rottype)) then da := transtype;
if (d = da) or ((d = rottype) and (da = transtype)) then
checkArg := n (* it's fine *)
else if da = undeftype then
begin (* need to define the variable *)
n↑.vari↑.vtype := d;
checkArg := n; (* but it's fine *)
end
else
begin (* no good - need to fix things up *)
pp10L(' Found a ',9); ppDtype(da);
pp10(' where a ',9); ppDtype(d);
pp20(' should have been. ',18);
ppLine;
bad := newNode;
with bad↑ do
begin
ntype := exprnode;
op := badop;
arg1 := n;
arg2 := defNode(d);
arg3 := nil;
end;
checkArg := bad;
end;
end;
end;
function copyExpr (* (n: nodep; lcp: boolean): nodep; *);
var np: nodep;
begin
if n = nil then np := nil
else
with n↑ do
begin
if (ntype <> leafnode) or (ltype = varitype) or lcp then
begin (* need to make a copy *)
np := newNode;
np↑.ntype := ntype;
case ntype of
arraydefnode:
begin
np↑.numdims := numdims;
np↑.combnds := true; (* indicate it's a copy *)
np↑.bounds := copyexpr(bounds,false);
end;
bnddefnode:
begin
np↑.next := copyexpr(next,false);
np↑.lower := copyexpr(lower,false);
np↑.upper := copyexpr(upper,false);
end;
exprnode:
begin
np↑.op := op;
if op = arefop then lcp := true;
np↑.arg1 := copyexpr(arg1,false);
np↑.arg2 := copyexpr(arg2,lcp);
np↑.arg3 := copyexpr(arg3,false);
end;
leafnode:
begin
np↑.ltype := ltype;
np↑.length := length; (* this should work for all leaftypes *)
np↑.str := str
end;
listnode:
begin
np↑.lval := copyexpr(lval,lcp);
np↑.next := copyexpr(next,lcp);
end;
end
end
else np := n;
end;
copyExpr := np;
end;
procedure ppFlush;
begin
pp20(' Will flush statemen',20); ppChar('t');
end;
(* aux routines for parsing expressions(cont): getargs *)
procedure getargs(opn: nodep);
var arg,n,np,nhdr,d: nodep; nargs,i: integer; dch: ascii; dat: datatypes;
absp,aref,func,qp,closep,b,bp: boolean; paramlist,v: varidefp;
procedure check1(d: datatypes);
begin
opn↑.arg1 := checkarg(opn↑.arg1,d); (* check datatype is right *)
end;
procedure check2(d1,d2: datatypes);
begin
with opn↑ do
begin
arg1 := checkarg(arg1,d1); (* check datatype is right for first arg *)
arg2 := checkarg(arg2,d2); (* and also check second *)
end;
end;
procedure check3(d1,d2,d3: datatypes);
begin
with opn↑ do
begin
arg1 := checkarg(arg1,d1); (* check datatype is right for first arg *)
arg2 := checkarg(arg2,d2); (* and also check second *)
arg3 := checkarg(arg3,d3); (* and also check third *)
end;
end;
begin
with opn↑ do
begin
if not ((op=arefop) or (op=callop)) then arg1 := nil;
arg2 := nil;
arg3 := nil
end;
if (opn↑.op = grinchop) then (* grinch is special *)
begin
if curMotion <> nil then
opn↑.arg1 := copyExpr(curMotion↑.cf,true) (* copy control frame *)
else
begin
pp20L('Grinch can only occu',20); pp20('r in a motion statem',20);
pp5('ent ',3);
errprnt;
opn↑.op := badop;
opn↑.arg1 := newNode;
opn↑.arg2 := defNode(transtype);
with opn↑.arg1↑ do
begin
ntype := exprnode;
op := grinchop;
arg1 := opn↑.arg2;
arg2 := nil;
arg3 := nil;
end
end
end
else if (opn↑.op <> inscalarop) then (* expecting some args *)
begin
i := 0;
nhdr := nil;
d := nil;
nargs := 1;
absp := false;
aref := false;
func := false;
qp := false;
closep := true;
b := true;
paramlist := nil;
case opn↑.op of
atan2op,
tmakeop,
fmakeop,
vsaxwrop,
dacop: nargs := 2;
vmakeop,
constrop: nargs := 3;
queryop: begin
qp := true;
nargs := 99; (* variable number of args *)
end;
absop: absp := true;
arefop: begin
aref := true;
n := opn↑.arg1↑.vari↑.a; (* check it's defined *)
if n = nil then nargs := 1 else nargs := n↑.numdims;
end;
callop: begin
func := true;
nargs := 0;
n := opn↑.arg1↑.vari↑.p; (* see if procedure is defined *)
if n <> nil then
begin
paramlist := n↑.paramlist;
if paramlist = nil then closep := false;
end;
end;
end;
if not absp then
begin
getToken; (* looking for opening '(' or '[' *)
if aref then dch := '[' else dch := '(';
with curToken do
if (ttype <> delimtype) or (ch <> dch) then (* not there - complain *)
begin
backup := true;
if opn↑.op = timeop then
begin
b := false; (* don't bother looking for args *)
closep := false; (* so we know not to expect a closing ')' *)
opn↑.arg1 := defNode(svaltype); (* use zero *)
i := 1;
end
else if qp or not closep then (* query doesn't need to take any args *)
begin
b := false; (* don't bother looking for args *)
closep := false; (* so we know not to expect a closing ')' *)
end
else
begin
pp10L('Need a " ',8); ppChar(dch); pp10('" here ',6);
errprnt;
end;
end;
end;
while b do
begin (* get the next argument *)
if paramlist = nil then arg := exprParse (* implies (not func) *)
else if paramlist↑.tbits <> 5 then arg := exprParse
else
with curToken do
begin (* looking for array passed by reference *)
getToken;
bp := ttype = identtype;
if bp then
begin (* is it a defined variable and an array? *)
v := varLookup(id);
if v <> nil then bp := (v↑.vtype <> pconstype) and odd(v↑.tbits)
else bp := false;
end;
if bp then
begin
arg := newNode;
arg↑.ntype := leafnode;
arg↑.ltype := varitype;
arg↑.vari := v;
arg↑.vid := v↑.name;
end
else (* no good *)
begin
pp20L('Need an array variab',20); pp10('le here ',7);
errprnt;
arg := nil;
end;
end;
if arg <> nil then (* got one *)
begin
i := i + 1;
if func or aref or qp then (* add to arg list *)
begin
np := newNode;
np↑.ntype := listnode;
if func and (paramlist <> nil) then
with paramlist↑ do
begin (* check parameter for correct data type *)
np↑.lval := checkarg(arg,vtype);
if dtype <> nil then d := dtype↑.dim (* use dimension if it exists *)
else (* otherwise use default *)
if (vtype = transtype) or (vtype = frametype) then
d := distancedim↑.dim
else if vtype = rottype then d := angledim↑.dim
else d := nodim↑.dim;
checkdim(arg,d);
d := nil;
paramlist := next;
if paramlist = nil then nargs := i;
end
else if aref then
begin
np↑.lval := checkarg(arg,svaltype);
checkdim(arg,nodim↑.dim);
end
else np↑.lval := arg;
if nhdr = nil then nhdr := np else n↑.next := np;
n := np;
n↑.next := nil;
end
else
begin
with opn↑ do
case i of
1: arg1 := arg;
2: arg2 := arg;
3: arg3 := arg;
end;
end;
getToken; (* looking for separating ',' *)
with curToken do
if (ttype <> delimtype) or (ch <> ',') then b := false (* that's it *)
end
else b := false;
end;
if absp then (* looking for closing '|' *)
begin
with curToken do
if (ttype <> reswdtype) or (rtype <> optype) or (op <> absop) then
begin (* not there - complain *)
backup := true;
pp10('Need a " ',8); ppChar(chr(174B)); pp10('" here ',6);
errprnt;
end;
if opn↑.arg1 = nil then opn↑.arg1 := defNode(svaltype);
dat := getdtype(opn↑.arg1); (* now figure out what sort of || we've got *)
if dat = svaltype then opn↑.op := sabsop
else if dat = vectype then opn↑.op := vmagnop
else opn↑.op := tmagnop;
end
else if closep then
begin
if aref then dch := ']' else dch := ')';
backup := true; (* looking for closing ')' or ']' *)
getDelim(dch);
end
else backup := true;
if func or aref then (* store arg list in arg 2 *)
begin
while (i < nargs) or (paramlist <> nil) do
begin (* make sure we return the right size arg list *)
i := i + 1;
np := newNode;
np↑.ntype := listnode;
if func and (paramlist <> nil) then
begin
np↑.lval := defNode(paramlist↑.vtype);
paramlist := paramlist↑.next;
if paramlist = nil then nargs := i;
end
else np↑.lval := defNode(svaltype);
if nhdr = nil then nhdr := np else n↑.next := np;
n := np;
n↑.next := nil;
end;
opn↑.arg2 := nhdr;
end
else if qp then opn↑.arg2 := nhdr (* store arg list in arg 2 *)
else
with opn↑ do
case op of (* check args are of proper type & dimension *)
sqrtop: check1(svaltype);
logop,
expop,
asinop,
acosop,
adcop: begin
check1(svaltype);
checkdim(arg1,nodim↑.dim);
end;
timeop: begin
check1(svaltype);
checkdim(arg1,timedim↑.dim);
end;
sinop,
cosop,
tanop: begin
check1(svaltype);
checkdim(arg1,angledim↑.dim);
end;
dacop,
atan2op: begin
check2(svaltype,svaltype);
checkdim(arg1,nodim↑.dim);
checkdim(arg2,nodim↑.dim);
end;
vmakeop: begin
check3(svaltype,svaltype,svaltype);
checkdim(arg2,getdim(arg1,d));
checkdim(arg3,d);
end;
unitvop: check1(vectype);
vsaxwrop: begin
check2(vectype,svaltype);
checkdim(arg2,angledim↑.dim);
end;
tposop,
torientop,
tinvrtop: check1(transtype);
taxisop: check1(rottype);
fmakeop,
tmakeop: begin
check2(rottype,vectype);
checkdim(arg1,angledim↑.dim);
if op = fmakeop then checkdim(arg2,distancedim↑.dim);
end;
deproachop: begin
check1(frametype);
checkdim(arg1,distancedim↑.dim);
end;
constrop: begin
check3(vectype,vectype,vectype);
checkdim(arg1,distancedim↑.dim);
checkdim(arg2,distancedim↑.dim);
checkdim(arg3,distancedim↑.dim);
end;
end;
if aref then (* if array, check it's defined *)
if opn↑.arg1↑.vari↑.a = nil then nargs := i; (* it's not, assume all ok *)
if (not qp) and (i <> nargs) then
begin
pp10L('Need ',4); ppInt(nargs); pp20(' arguments here ',15);
errprnt;
end;
if d <> nil then relNode(d); (* done with dimension node *)
end;
end;
(* function to parse expressions: exprParse *)
function exprParse; (* : nodep *)
var expstack, opstack: nodep; precstack: array [0..10] of integer;
opsp,i,j: integer; n,np: nodep; vp: varidefp; opseen,done,badp: boolean;
function badexpr: nodep;
var n: nodep;
begin
n := newNode;
badexpr := n;
with n↑ do
begin ntype:= exprnode; op:= badop; arg1:= nil; arg2:= newNode; arg3:= nil end;
n := n↑.arg2;
with n↑ do begin ntype := leafnode; ltype := transtype; t := niltrans end;
if not badp then
begin
pp20L('Bad expression ',14);
errprnt;
badp := true;
end;
end;
function gettype(n: nodep): datatypes;
var d: datatypes;
begin
d := getdtype(n);
if (d = rottype) or (d = frametype) then d := transtype;
gettype := d;
end;
procedure pushexp(n: nodep);
begin
n↑.next := expstack;
expstack := n;
end;
procedure cpushexp(n: nodep);
begin
if opseen then pushexp(n) (* all okay *)
else
begin (* yow! - we just saw an operand - complain *)
pp20L('Bad expression - con',20); pp20('secutive operands ',17);
errprnt;
end;
opseen := false; (* expecting an operator *)
end;
function popexp: nodep;
var n: nodep;
begin
if expstack <> nil then
begin
n := expstack;
expstack := expstack↑.next;
n↑.next := nil;
popexp := n;
end
else
begin (* this probably can't happen, but... *)
pp20L('Gack! - parse operan',20); pp20('d expression stack u',20);
pp10('nderflow ',8);
errprnt;
popexp := badexpr;
end;
end;
procedure pushop;
begin
if opsp <= 9 then
begin
n↑.next := opstack;
opstack := n;
opsp := opsp + 1;
precstack[opsp] := i;
end
else
begin
pp20L('Gack! - parse operat',20); pp20('or expression stack ',20);
pp10('overflow ',8);
errprnt;
end;
opseen := true; (* expecting an operand *)
end;
procedure popop;
var n,n1,d: nodep; d1,d2: datatypes;
begin
d := nil;
n := opstack;
opstack := n↑.next;
opsp := opsp - 1;
with n↑ do
begin (* get its operand(s) *)
next := nil;
arg3 := nil;
if (op = negop) or (op = notop) then arg2 := nil
else
begin
arg2 := popexp;
if expstack = nil then
begin (* whoops - wasn't any arg 2 *)
expstack := arg2;
arg2 := badexpr;
end;
end;
arg1 := popexp;
if op <= modop then
begin
arg1 := checkarg(arg1,svaltype); (* check datatypes of args *)
if op <> notop then arg2 := checkarg(arg2,svaltype);
if (op <= sneop) or (op >= maxop) then (* relation, max, min & mod *)
begin
if (op <> intop) and (op <> idivop) then (* don't care about these *)
checkdim(arg2,getdim(arg1,d)); (* does arg2 match dimension of arg1 *)
end
else if op <= sexpop then (* check dimensions too *)
begin (* args better be dimensionless *)
checkdim(arg1,nodim↑.dim);
if op <> notop then checkdim(arg2,nodim↑.dim);
end
end
else if op = vdotop then
begin
arg1 := checkarg(arg1,vectype);
arg2 := checkarg(arg2,vectype);
end
else if op = wrtop then
begin
arg1 := checkarg(arg1,vectype);
arg2 := checkarg(arg2,transtype);
end
else if op = ftofop then
begin
arg1 := checkarg(arg1,transtype);
arg2 := checkarg(arg2,transtype);
checkdim(arg2,getdim(arg1,d)); (* does arg2 match dimension of arg1 *)
end
else if op >= addop then (* need to determine proper op for given args *)
case op of
negop: begin (* see if snegop or vnegop *)
d1 := getdtype(arg1);
if d1 = svaltype then op := snegop
else if d1 = vectype then op := vnegop
else begin n1 := badexpr; n1↑.arg1 := n; n := n1 end;
end;
addop: begin
checkdim(arg2,getdim(arg1,d)); (* does arg2 match dimension of arg1 *)
d1 := gettype(arg1);
d2 := gettype(arg2);
if d1 = undeftype then begin d1 := d2; arg1↑.vari↑.vtype := d1 end;
if d2 = undeftype then
begin
if d1 = transtype then d2 := vectype else d2 := d1;
arg2↑.vari↑.vtype := d2
end;
if (d1 = svaltype) and (d2 = svaltype) then op := saddop
else if (d1 = vectype) and (d2 = vectype) then op := vaddop
else if (d1 = transtype) and (d2 = vectype) then op := tvaddop
else begin op := saddop; n1 := badexpr; n1↑.arg1 := n; n := n1 end;
end;
subop: begin
checkdim(arg2,getdim(arg1,d)); (* does arg2 match dimension of arg1 *)
d1 := gettype(arg1);
d2 := gettype(arg2);
if d1 = undeftype then begin d1 := d2; arg1↑.vari↑.vtype := d1 end;
if d2 = undeftype then
begin
if d1 = transtype then d2 := vectype else d2 := d1;
arg2↑.vari↑.vtype := d2
end;
if (d1 = svaltype) and (d2 = svaltype) then op := ssubop
else if (d1 = vectype) and (d2 = vectype) then op := vsubop
else if (d1 = transtype) and (d2 = vectype) then op := tvsubop
else begin op := ssubop; n1 := badexpr; n1↑.arg1 := n; n := n1 end;
end;
mulop: begin
d1 := gettype(arg1);
d2 := gettype(arg2);
if d1 = undeftype then begin d1 := d2; arg1↑.vari↑.vtype := d1 end;
if d2 = undeftype then begin d2 := d1; arg2↑.vari↑.vtype := d2 end;
if (d1 = svaltype) and (d2 = svaltype) then op := smulop
else if (d1 = svaltype) and (d2 = vectype) then op := svmulop
else if (d1 = vectype) and (d2 = svaltype) then op := vsmulop
else if (d1 = vectype) and (d2 = vectype) then op := crossvop
else if (d1 = transtype) and (d2 = vectype) then op := tvmulop
else if (d1 = transtype) and (d2 = transtype) then op := ttmulop
else begin op := smulop; n1 := badexpr; n1↑.arg1 := n; n := n1 end;
if (op = ttmulop) or (op = tvmulop) then
if getdtype(arg1) <> rottype then
checkdim(arg2,getdim(arg1,d)); (* does arg2 match dimension of arg1 *)
end;
divop: begin
d1 := gettype(arg1);
d2 := gettype(arg2);
if d1 = undeftype then
begin d1 := svaltype; arg1↑.vari↑.vtype := d1 end;
if d2 = undeftype then
begin d2 := svaltype; arg2↑.vari↑.vtype := d2 end;
if (d1 = svaltype) and (d2 = svaltype) then op := sdivop
else if (d1 = vectype) and (d2 = svaltype) then op := vsdivop
else begin op := sdivop; n1 := badexpr; n1↑.arg1 := n; n := n1 end;
end;
end;
pushexp(n); (* save it as operand for next operator *)
if d <> nil then relNode(d);
end;
end;
function opprecedence(op: exprtypes): integer;
var i: integer;
begin
i := 0;
case op of
eqvop: i := 1;
orop,
xorop: i := 2;
andop: i := 3;
sltop,
sleop,
seqop,
sgeop,
sgtop,
sneop: i := 4;
addop,
subop: i := 5;
wrtop: i := 6;
mulop,
divop,
maxop,
minop,
idivop,
modop,
vdotop: i := 7;
sexpop,
ftofop: i := 8;
negop,
notop: i := 9;
end;
opprecedence := i;
end;
begin
expstack := nil;
opstack := nil;
opsp := 0;
precstack[0] := -1;
done := false;
opseen := true; (* expecting an operand *)
badp := false; (* haven't complained about expression yet *)
repeat
getToken;
with curToken do
begin
case ttype of (* see what we've got *)
labeldeftype:
begin done := true; backup := true end;
delimtype:
if ch = '(' then
begin
cpushexp(exprParse); (* get the parenthesized expression *)
getDelim(')'); (* get the closing ')' *)
end
else begin done := true; backup := true end;
reswdtype:
if rtype <> optype then begin done := true; backup := true end
else if not opseen and (op = absop) then
begin done := true; backup := true end
else if not (opseen and (op = addop)) then (* we want to ignore unary + *)
begin
if opseen and (op = subop) then op := negop;
n := newNode;
n↑.ntype := exprnode;
n↑.op := op;
i := opprecedence(op);
if i = 0 then (* really an operand *)
begin
getargs(n); (* get any arguments op needs *)
cpushexp(n); (* save operand for its operator *)
end
else if opseen and ((op <> negop) and (op <> notop)) then
begin (* yow! - we just saw an operator - complain *)
pp20L('Bad expression - con',20); pp20('secutive operators ',18);
errprnt;
end
else if i > precstack[opsp] then (* higher precedence so push on stack *)
pushop
else (* lower precedence *)
begin
while (i <= precstack[opsp]) and (i < 9) do popop; (* 9 = prec(not,neg) *)
pushop;
end;
end;
constype: cpushexp(cons);
identtype:
begin
vp := varLookup(id);
if vp = nil then
begin (* undefined variable *)
vp := makeUVar(undeftype,id); (* define it somewhat *)
i := curChar;
getToken; (* see if it's supposed to be a procedure or array *)
backup := true; (* we're just peeking *)
pp10L(' Undeclare',10);
if (ttype = delimtype) and ((ch = '(') or (ch = '[')) then
if ch = '[' then
begin
vp↑.tbits := 1; (* array *)
vp↑.a := nil;
pp20('d array variable ',16);
end
else
begin
vp↑.tbits := 2; (* procedure *)
vp↑.p := nil;
pp20('d procedure ',11);
end
else pp10('d variable',10);
pp20(' - will try to defin',20); pp5('e it.',5);
j := curChar;
curChar := i; (* use where we were before we peeked *)
errprnt;
curChar := j;
end;
if vp↑.vtype = pconstype then (* constant *)
begin
np := newNode; (* need to make a pointer to it *)
with np↑ do
begin
ntype := leafnode;
ltype := pconstype;
cname := vp;
pcval := vp↑.c;
end;
cpushexp(np);
end
else if odd(vp↑.tbits) or (vp↑.tbits = 2) then
begin (* array reference or procedure call *)
n := newNode;
with n↑ do
begin
ntype := exprnode;
if odd(vp↑.tbits) then op := arefop else op := callop;
arg1 := newNode;
end;
with n↑.arg1↑ do
begin
ntype := leafnode;
ltype := varitype;
vari := vp;
vid := vp↑.name;
end;
getargs(n); (* get subscripts/parameters *)
cpushexp(n);
end
else (* variable *)
begin
n := newNode;
with n↑ do
begin
ntype := leafnode;
ltype := varitype;
vari := vp;
vid := vp↑.name;
end;
cpushexp(n);
end;
end;
end;
end;
until done;
while opsp > 0 do popop; (* bind the rest of the operators *)
if expstack <> nil then exprParse := popexp (* return what's left on stack *)
else exprParse := nil;
while expstack <> nil do relNode(popexp); (* probably don't need, but... *)
end;
(* auxiliary expression mungers: relExpr & evalOrder *)
procedure relExpr(n: nodep);
var b: boolean; st,stp: strngp;
begin
b := true;
if n = nil then b := false
else
with n↑ do
case ntype of
exprnode: begin
relExpr(arg1);
relExpr(arg2);
relExpr(arg3);
end;
leafnode: case ltype of
vectype: if v↑.refcnt <= 1 then relVector(v)
else v↑.refcnt := v↑.refcnt - 1;
transtype: if t↑.refcnt <= 1 then relTrans(t)
else t↑.refcnt := t↑.refcnt - 1;
strngtype: if (length <> 2) or (str↑.ch[1] <> chr(15B)) or
(str↑.ch[2] <> chr(12B)) then
begin
st := str;
while st <> nil do
begin stp := st↑.next; relStrng(st); st := stp end;
end
else b := false;
end;
listnode: begin
relExpr(lval);
relExpr(next);
end;
ffnode: begin
if pdef then relNode(ff)
else relExpr(ff);
end;
forcenode:begin
relExpr(fval);
relExpr(fvec);
relExpr(fframe);
end;
arraydefnode: relExpr(bounds);
bnddefnode:begin
relExpr(lower);
relExpr(upper);
relExpr(next);
end;
end;
if b then relNode(n);
end;
function evalOrder(what,last: nodep; pcons: boolean): nodep;
var vp: varidefp; n: nodep; tbits: integer;
begin
if what <> nil then
with what↑ do
case ntype of
exprnode:
if (op < ioop) or (op = adcop) or (op = dacop) then
begin (* regular ops are easy to handle *)
next := last;
last := evalOrder(arg1,what,false); (* all ops have at least one arg *)
if arg2 <> nil then last := evalOrder(arg2,last,false);
if arg3 <> nil then last := evalOrder(arg3,last,false);
end
else if (op = grinchop) then last := evalOrder(arg1,last,true)
else if op < specop then (* query or inscalar *)
begin
what↑.next := last;
if op = inscalarop then last := what (* inscalar has no args *)
else if arg2 = nil then last := what (* query has no print list *)
else last := evalOrder(arg2,what,false); (* handle query's print list *)
end
else if op = arefop then
begin
arg1↑.next := last;
last := evalOrder(arg2,arg1,true); (* need to push constants too *)
end
else if op = callop then
begin
what↑.next := last;
last := what;
if arg2 <> nil then
begin
with arg1↑.vari↑ do
if p <> nil then vp := p↑.paramlist else vp := nil;
n := arg2;
while n <> nil do
begin (* evaluate parameters *)
if vp <> nil then
begin
tbits := vp↑.tbits;
vp := vp↑.next;
end
else tbits := 0;
with n↑.lval↑ do
begin
if (tbits = 4) then (* call by reference *)
if ((ntype = exprnode) and (op <> arefop)) or (* expression *)
((ntype = leafnode) and (ltype <> varitype)) (* constant *)
then tbits := 0; (* change to call by value *)
if tbits = 0 then last := evalOrder(n↑.lval,last,false)
else if (tbits = 4) and (ntype = exprnode) then
last := evalOrder(arg2,last,true); (* push subscripts *)
end;
n := n↑.next;
end
end
end
else if op = badop then (* stick default value node so it goes on stack *)
begin
arg2↑.next := last;
last := arg2;
end;
listnode:
begin
last := evalOrder(lval,last,pcons); (* set up this list element's value *)
if next <> nil then
last := evalOrder(next,last,pcons); (* now move down the list *)
end;
bnddefnode:
begin
last := evalOrder(lower,last,false); (* set up this subscript's values *)
last := evalOrder(upper,last,false);
if next <> nil then
last := evalOrder(next,last,false); (* now move down the list *)
end;
leafnode:
if pcons or (ltype = varitype) then
begin (* get variable's value or if asked push constants *)
next := last;
last := what;
end;
durnode:
last := evalOrder(durval,last,false); (* evaluate duration value *)
deprnode,
apprnode,
destnode:
begin
last := evalOrder(loc,last,false); (* evaluate location *)
if code <> nil then
if code↑.stype = signaltype then
if code↑.event↑.ntype <> leafnode then
last := evalOrder(code↑.event↑.arg2,last,true);
end;
viaptnode:
begin
last := evalOrder(via,last,false); (* evaluate location *)
if duration <> nil then
last := evalOrder(duration,last,false); (* evaluate duration *)
if velocity <> nil then
last := evalOrder(velocity,last,false); (* evaluate velocity *)
if vcode <> nil then
if vcode↑.stype = signaltype then
if vcode↑.event↑.ntype <> leafnode then
last := evalOrder(vcode↑.event↑.arg2,last,true);
end;
forcenode:
begin
last := evalOrder(fval,last,false); (* evaluate force value *)
end;
end;
evalOrder := last;
end;
(* aux routines for parsing blocks: getDeclarations & checkBlkids *)
function getDeclarations(pdef: boolean; lev: integer;
var vo: varidefp; var numvars: integer): varidefp;
var vhdr,va,vp,vdim: varidefp; off,tb,i: integer; d: datatypes;
endlist,b: boolean; no,n: nodep; idname: identp;
function declarationp: boolean;
var b: boolean; v: varidefp;
begin
b := false;
getToken;
with curToken do
if ttype = reswdtype then
begin
if rtype = decltype then b := true
else if (rtype = optype) and ((op = vmakeop) or (op = vsaxwrop) or
(op = tmakeop) or (op = fmakeop)) then
begin
b := true;
rtype := decltype;
if op = vmakeop then decl := vectype
else if op = vsaxwrop then decl := rottype
else if op = tmakeop then decl := transtype else decl := frametype;
end
else if ((rtype = clsetype) and
((clause = forcetype) or (clause = torquetype) or
(clause = angularvelocitytype) or (clause = velocitytype))) then
begin
b := true;
ttype := identtype;
if clause = forcetype then id := forcedim↑.name
else if clause = torquetype then id := torquedim↑.name
else if clause = velocitytype then id := veldim↑.name
else id := angveldim↑.name;
end
end
else if ttype = identtype then
begin
v := varLookup(id);
if v <> nil then b := v↑.vtype = dimensiontype else b := false;
end;
if not b then backup := true;
declarationp := b;
end;
begin
numvars := 0;
if vo = nil then off := 0 else off := vo↑.offset + 1;
vhdr := nil;
if declarationp then (* any declarations? *)
with curToken do
begin
flushcomments := true; (* don't allow comments here *)
b := true;
if pdef then
if (ttype = reswdtype) and (rtype = decltype) and
((decl = reftype) or (decl = valtype)) then
begin (* "reference" or "value" procedure args *)
if decl = valtype then tb := 0 else tb := 4;
b := declarationp; (* get dimension or base type *)
end
else tb := 4 (* pass by "reference" is the default *)
else tb := 0;
if (ttype = identtype) and b then
begin (* deal with dimension info *)
vdim := varLookup(id); (* save it for later *)
b := declarationp; (* get base datatype *)
end
else vdim := nil;
if (not b) or (ttype <> reswdtype) or (rtype <> decltype) or
(decl > arraytype) then
begin (* not a valid basic datatype *)
pp20L('Need a basic datatyp',20); pp20('e here - flushing ti',20);
pp20('l next semicolon ',16);
errprnt;
while (ttype <> delimtype) or (ch <> ';') do getToken; (* flush tokens *)
end
else
begin
if decl <> arraytype then d := decl
else
begin
d := undeftype; (* define it later *)
backup := true;
pp20L('Need to specify base',20); pp20(' type of array - wil',20);
pp20('l try to define it l',20); pp5('ater ',4);
errprnt;
end;
if d <> proctype then getToken; (* is this really an array or procedure? *)
if (ttype = reswdtype) and (rtype = decltype) and (decl = proctype) then
begin (* procedure definition *)
getToken; (* get the procedure's name *)
if ttype <> identtype then
begin (* garbage *)
pp20L('Expecting an identif',20); pp10('ier here ',8);
errprnt;
backup := true;
idname := nil;
end
else idname := id;
vp := newVaridef;
if vhdr = nil then
begin
vhdr := vp;
if (vo = nil) and (not pdef) then curBlock↑.variables := vhdr;
end;
if vo <> nil then
with vo↑ do next := vp; (* add to list *)
vo := vp;
vp := curProc; (* save any outer procedure *)
with vo↑ do
begin
next := nil;
dnext := vp; (* hack to stack any nested proc defs *)
name := idname;
level := lev;
offset := off;
off := off + 1;
numvars := numvars + 1;
tbits := 2;
if d = proctype then vtype := nulltype else vtype := d;
dtype := vdim;
n := newNode;
p := n;
end;
with n↑ do
begin
ntype := procdefnode;
ptype := vo↑.vtype;
level := lev + 1;
pname := vo;
getToken; (* see if procedure has any parameters *)
paramlist := nil;
if (ttype = delimtype) and (ch = '(') then (* yup - get 'em *)
begin
va := nil;
repeat
vdim := getDeclarations(true,level,va,i);
if paramlist = nil then paramlist := vdim;
until i = 0;
flushcomments := true;
getDelim(')'); (* look for closing ")" *)
end
else backup := true;
getDelim(';'); (* get separating ";" *)
curProc := vo;
body := stmntParse; (* get the body of the procedure *)
body↑.next := newStatement; (* append a return, just in case *)
with body↑.next↑ do
begin
stype := returntype;
retval := nil;
exprs := nil;
last := n↑.body;
rproc := n;
end;
end;
curProc := vp; (* restore outer procedure, if any *)
if not semiseen then getDelim(';');
end
else
begin
if (ttype = reswdtype) and (rtype = decltype) and (decl = arraytype) then
begin
tb := tb + 1; (* we've got an array specification *)
va := nil; (* for list of arrays sharing common bounds list *)
if pdef and (tb = 1) then
begin
tb := 5;
pp20L('Can''t pass arrays by',20); pp20(' value - changing to',20);
pp10(' reference',10);
errprnt;
end
end
else
begin
backup := true;
if pdef and (tb = 0) and (d = eventtype) then
begin
tb := 4;
pp20L('Can''t pass events by',20); pp20(' value - changing to',20);
pp10(' reference',10);
errprnt;
end
end;
if vdim <> nil then (* check that dimension applies to base type *)
begin
b := false;
if (d = rottype) and not matchdim(vdim↑.dim,angledim↑.dim,true) then
begin
b := true;
pp20L('Rotations must be of',20); pp20(' dimension ANGLE ',16);
end
else if (d = frametype) and
not matchdim(vdim↑.dim,distancedim↑.dim,true) then
begin
b := true;
pp20L('Frames must be of di',20); pp20('mension DISTANCE ',16);
end;
if b then
begin
errprnt;
vdim := nil;
end
end;
repeat
endlist := true; (* assume this is last one *)
getToken; (* declare the new variables *)
if ttype <> identtype then
begin (* garbage *)
pp20L('Expecting an identif',20); pp10('ier here ',8);
errprnt;
backup := true;
end
else
begin
vp := newVaridef;
if vhdr = nil then
begin
vhdr := vp;
if (vo = nil) and (not pdef) then curBlock↑.variables := vhdr;
end;
if vo <> nil then
with vo↑ do next := vp; (* add to list *)
vo := vp;
if id↑.predefined <> nil then
if id↑.predefined↑.vtype = pconstype then
begin
pp20L('Redefining predeclar',20); pp20('ed constant - not a ',20);
pp10('good idea ',9);
errprnt;
end;
with vp↑ do
begin
next := nil;
dnext := nil;
name := id;
level := lev;
offset := off;
off := off + 1;
numvars := numvars + 1;
tbits := tb;
vtype := d;
dtype := vdim;
if d = labeltype then s := nil;
end;
if odd(tb) then
begin (* look for array bounds *)
getToken; (* looking for a "[" *)
if (ttype <> delimtype) or (ch <> '[') then
begin (* not yet *)
backup := true;
vp↑.a := nil; (* no bounds info yet *)
if va = nil then va := vp; (* so we can fill things in later *)
if (ttype = delimtype) and (ch = ';') then
begin (* we aren't going to get one *)
va := nil;
if not pdef then
begin
pp20L('Expecting an array b',20); pp20('ounds list here ',15);
errprnt;
end
end
end
else
begin (* got one *)
vp↑.a := newNode;
vp↑.a↑.ntype := arraydefnode;
vp↑.a↑.combnds := false;
no := nil;
i := 0;
repeat
n := newNode;
i := i + 1;
with n↑ do
begin
ntype := bnddefnode;
next := nil;
lower := exprParse; (* get lower bound definition *)
getDelim(':'); (* looking for separating ":" *)
upper := exprParse; (* get upper bound definition *)
getToken; (* looking for final "]" or separating "," *)
if (ttype <> delimtype) or ((ch <> ',') and (ch <> ']')) then
begin
pp20L('Expecting a "," or "',20); pp10(']" here ',7);
errprnt;
backup := true;
end;
if no = nil then vp↑.a↑.bounds := n else no↑.next := n;
no := n;
end
until ((ttype = delimtype) and ((ch = ']') or (ch = ';'))) or
(ttype = reswdtype);
vp↑.a↑.numdims := i;
while va <> nil do (* now we can fill things in *)
begin
va↑.a := copyexpr(vp↑.a,false); (* copy bounds info *)
va := va↑.next;
if va↑.next = nil then va := nil; (* we already got this one *)
end
end
end;
getToken; (* looking for "," or ";" or ")" *)
if ttype = delimtype then
begin
if ch = ',' then endlist := false (* more to get *)
else if pdef and (ch = ')') then backup := true
else if ch <> ';' then
begin
pp20L('Expecting a "," or "',20); pp10(';" here ',7);
errprnt;
end
end
else
begin
backup := true;
pp20L('Inserting missing " ',19);
if ttype = identtype then (* user defined dimension type? *)
begin
vp := varLookup(id);
if vp = nil then endlist := false
else if vp↑.vtype <> dimensiontype then endlist := false;
end;
if endlist then pp5(';" ',2)
else pp5('," ',2);
errprnt;
end
end
until endlist;
end
end;
flushcomments := false; (* comments are ok again *)
end;
getDeclarations := vhdr;
end;
procedure checkblkids(id1,id2: identp);
var c1,c2: ascii; i,j,len: integer; b: boolean; s1,s2: strngp;
begin
if (id1 <> nil) and (id2 <> nil) then
begin
len := id1↑.length;
b := len = id2↑.length; (* make sure both strings are the same length *)
s1 := id1↑.name;
s2 := id2↑.name;
i := 0;
j := 1;
while b and (i < len) do
begin
c1 := upperCase(s1↑.ch[j]);
c2 := upperCase(s2↑.ch[j]);
if c1 <> c2 then b := false
else
begin
i := i + 1;
if j < 10 then j := j + 1
else begin j := 1; s1 := s1↑.next; s2 := s2↑.next end;
end;
end;
if not b then
begin
pp20L('Block ids do not mat',20); pp5('ch ',2);
errprnt;
end;
end;
end;
function blockParse(st: statementp): boolean;
var b,bs: boolean; so,sp: statementp; bid: identp;
oldVariable,v,vhdr,vo: varidefp; i: integer; lexp: nodep;
begin (* block statement *)
b := false; (* no way(?) we can lose here *)
flushcomments := false; (* in trouble if comment before id, but... *)
getToken; (* any block id? *)
with curToken do
begin
st↑.blkid := nil;
if ttype = constype then
begin
if cons↑.ltype = strngtype then (* yup - grab the id string *)
begin
bid := newIdent;
bid↑.length := cons↑.length;
bid↑.name := cons↑.str;
st↑.blkid := bid;
end
else
begin
pp20L('Need a string here ',18);
errprnt;
end;
relNode(cons);
end
else backup := true;
end;
with st↑ do
begin
if curBlock = nil then level := 1 else level := curBlock↑.level + 1;
if curProc <> nil then (* may need to correct if outer block of proc *)
if curProc↑.p↑.level = level then level := level + 1;
bparent := curBlock;
bcode := nil;
end;
curBlock := st;
if outerBlock = nil then outerBlock := st;
oldVariable := curVariable;
curVariable := nil;
vhdr := nil;
st↑.variables := nil;
so := nil;
bs := true;
while bs do
begin
flushcomments := false; (* comments are ok here *)
vhdr := getDeclarations(false,st↑.level,curVariable,i); (* get any block vars *)
if i > 0 then
begin (* make a decl stmnt for data type *)
vo := vhdr;
v := vhdr↑.next;
while v <> nil do begin vo↑.dnext := v; vo := v; v := v↑.next end;
sp := newStatement;
with sp↑ do
begin
stype := declaretype;
numvars := i;
variables := vhdr;
end;
if so = nil then (* may have declared some undefined variables *)
begin so := st↑.bcode;
if so <> nil then while so↑.next <> nil do so := so↑.next end;
if so = nil then begin sp↑.last := st; st↑.bcode := sp end
else begin so↑.next := sp; sp↑.last := so end;
so := sp;
end
else
begin
endOk := 1;
sp := stmntParse; (* get the next statement *)
if sp↑.stype = emptytype then relStatement(sp) (* flush bad ones *)
else
begin
if so = nil then (* may have declared some undefined variables *)
begin so := st↑.bcode;
if so <> nil then while so↑.next <> nil do so := so↑.next end;
if so = nil then begin sp↑.last := st; st↑.bcode := sp end
else begin so↑.next := sp; sp↑.last := so end;
so := sp;
if sp↑.stype = endtype then (* we're all done *)
begin
bs := false;
sp↑.bparent := st;
sp↑.next := nil;
checkblkids(st↑.blkid,sp↑.blkid);
end
else
if not semiseen and (sp↑.stype <> commenttype) then
begin (* look for the separating ";" *)
getToken;
with curToken do
if (ttype <> delimtype) or (ch <> ';') then (* not there *)
begin
backup := true;
if not ((ttype = reswdtype) and (rtype = stmnttype) and
((stmnt = endtype) or (stmnt = commenttype))) then
begin
pp20('Inserting missing se',20); pp10('micolon ',7);
errprnt;
end;
end;
end;
end;
end;
end;
v := st↑.variables; (* need to pop block's variables from symbol table *)
i := 0; (* we can count number of them while we're at it too *)
lexp := nil;
while v <> nil do
begin
if v↑.tbits = 1 then lexp := evalOrder(v↑.a↑.bounds,lexp,false);
(* *** confirm that all labels that should be labelling cmons actually are *** *)
i := i + 1;
v := v↑.next;
end;
st↑.numvars := i;
st↑.exprs := lexp;
curVariable := oldVariable;
if curVariable <> nil then (* in case any undefined variables were declared *)
while curVariable↑.next <> nil do curVariable := curVariable↑.next;
curBlock := st↑.bparent;
blockParse := false; (* no way(?) we can lose here *)
end;
function coblockParse(st: statementp): boolean;
var b,bs,oldInCoblock: boolean; no,np: nodep; sp: statementp; bid: identp;
i: integer;
begin (* coblock statement *)
b := false; (* no way(?) we can lose here *)
getToken; (* any block id? *)
with curToken do
begin
st↑.cblkid := nil;
if ttype = constype then
begin
if cons↑.ltype = strngtype then (* yup - grab the id string *)
begin
bid := newIdent;
bid↑.length := cons↑.length;
bid↑.name := cons↑.str;
st↑.cblkid := bid;
end
else
begin
pp20L('Need a string here ',18);
errprnt;
end;
relNode(cons);
end
else backup := true;
end;
i := 0;
st↑.threads := nil;
oldInCoblock := inCoblock;
inCoblock := true; (* can't have any returns in threads *)
no := nil;
bs := true;
while bs do
begin
flushcomments := false; (* comments are ok here *)
coendOk := 1;
sp := stmntParse; (* get the next statement *)
sp↑.last := st; (* set up a back pointer *)
if sp↑.stype = emptytype then relStatement(sp) (* flush bad ones *)
else
if sp↑.stype = coendtype then (* we're all done *)
begin
bs := false;
st↑.nthreads := i;
sp↑.bparent := st;
sp↑.next := nil;
checkblkids(st↑.cblkid,sp↑.blkid);
np := st↑.threads; (* now have all threads point to the coend stmnt *)
while np <> nil do
begin
np↑.cstmnt↑.next := sp;
np := np↑.next
end;
end
else
begin
if sp↑.stype <> commenttype then i := i + 1;
np := newNode;
np↑.ntype := colistnode;
np↑.cstmnt := sp;
np↑.next := nil;
if no = nil then begin np↑.prev := nil; st↑.threads := np end
else begin no↑.next := np; np↑.prev := no end;
no := np;
if not semiseen then
begin
getToken; (* look for the separating ";" *)
with curToken do
if (ttype <> delimtype) or (ch <> ';') then (* not there *)
begin
backup := true;
if not ((sp↑.stype = commenttype) or
((ttype = reswdtype) and (rtype = stmnttype) and
((stmnt = coendtype) or (stmnt = commenttype)))) then
begin
pp20L('Inserting missing se',20); pp10('micolon ',7);
errprnt;
end;
end;
end;
end;
end;
inCoblock := oldInCoblock;
coblockParse := b;
end;
function endParse(st: statementp): boolean;
var bid: identp; b: boolean;
begin (* end or coend statement *)
st↑.blkid := nil;
if curchar + 2 < maxchar then
begin
getToken; (* any block id? *)
with curToken do
begin
if ttype = constype then
begin
if cons↑.ltype = strngtype then (* yup - grab the id string *)
begin
bid := newIdent;
bid↑.length := cons↑.length;
bid↑.name := cons↑.str;
st↑.blkid := bid;
end
else
begin
pp20L('Need a string here ',18);
errprnt;
end;
relNode(cons);
end
else backup := true;
end;
end;
if st↑.stype = endtype then b := endOk < 0
else b := coendOk < 0;
if b then
begin
pp20L('Can''t have an END/CO',20); pp10('END here ',8); ppLine;
errprnt;
if st↑.blkid <> nil then
begin freStrng(st↑.blkid↑.name); relIdent(st↑.blkid) end;
end;
endParse := b;
end;
function assignParse(st: statementp): boolean;
var d1,d2: datatypes; b: boolean; n1,n2,dim1,dim2: nodep;
begin (* assignment statement *)
b := false;
st↑.stype := assigntype;
st↑.aval := nil;
backup := true;
st↑.what := exprParse; (* what do we have? *)
with st↑.what↑ do
begin
n1 := nil;
if (ntype = leafnode) and (ltype = varitype) then n1 := st↑.what
else b := not ((ntype = exprnode) and
((op = callop) or (op = arefop) or (op = dacop)) );
if b and (ntype = exprnode) and
((op = tposop) or (op = torientop) or (op = deproachop)) then
if (arg1↑.ntype = leafnode) and (arg1↑.ltype = varitype) then
begin b := false; n1 := arg1 end
else b := not ((arg1↑.ntype = exprnode) and (arg1↑.op = arefop));
if n1 <> nil then (* make sure it's not a device *)
if n1↑.vari↑.level = 0 then
b := n1↑.vari↑.offset in [0,2,4,6,8,10,12,14,16,20];
(* offsets: arms: 0,4,8,12 hands: 2,6,10,14 driver/vise: 16,20 *)
if b then
begin (* no good *)
if n1 = nil then
begin pp20L('Can''t start a statem',20); pp20('ent this way ',12) end
else begin pp20L('Can''t assign values ',20); pp10('to devices',10) end;
errprnt;
end
else if (ntype = exprnode) and ((op = callop) or (op = dacop)) then
begin
if op = callop then st↑.stype := calltype;
st↑.exprs := evalOrder(st↑.what,nil,true);
end
else
begin
getToken; (* look for the ":=" *)
if (curToken.ttype <> reswdtype) or (curToken.rtype <> stmnttype) or
(curToken.stmnt <> assigntype) then
begin
b := true; (* no good *)
pp20L('Expecting a ":=" her',20); pp5('e. ',2); ppFlush;
errprnt;
relExpr(st↑.what);
end
else
begin (* so far so good *)
st↑.aval := exprParse;
d1 := getdtype(st↑.what);
d2 := getdtype(st↑.aval);
if d1 = undeftype then
begin
if (d2 = transtype) and (st↑.aval↑.ntype = exprnode) then
with st↑.aval↑ do (* check if it shouldn't really be a frame *)
if (op = constrop) or (op = fmakeop) then d2 := frametype
else if (ttmulop <= op) and (op <= tvsubop) then d2 := getDtype(arg1);
d1 := d2;
if st↑.what↑.ntype = leafnode then st↑.what↑.vari↑.vtype := d1
else st↑.what↑.arg1↑.vari↑.vtype := d1;
end;
if d2 = undeftype then
begin
d2 := d1;
if st↑.aval↑.ntype = leafnode then st↑.aval↑.vari↑.vtype := d2
else st↑.aval↑.arg1↑.vari↑.vtype := d2;
end;
if (d1 = frametype) or (d1 = rottype) then d1 := transtype;
if (d2 = frametype) or (d2 = rottype) then d2 := transtype;
if d1 <> d2 then
begin (* no good *)
b := true;
pp20L('Can''t assign a ',15); ppDtype(d2);
pp10(' to a ',6); ppDtype(d1);
ppChar('.'); ppFlush;
errprnt;
relExpr(st↑.what);
relExpr(st↑.aval);
end
else
begin (* determine order to evaluate expressions *)
if ntype = leafnode then n1 := nil
else if op = arefop then n1 := arg2
else if arg1↑.ntype = leafnode then n1 := nil else n1 := arg1↑.arg2;
if n1 = nil then n2 := nil
else n2 := evalorder(n1,nil,true); (* deal with subscripts *)
st↑.exprs := evalorder(st↑.aval,n2,true);
dim1 := nil; (* now check that dimensions match *)
dim2 := nil;
if not matchdim(getdim(st↑.aval,dim1),getdim(st↑.what,dim2),dimCheck) then
begin
pp20L('Dimensions don''t mat',20); pp20('ch in assignment sta',20);
pp10('tement ',6);
errprnt;
end;
relNode(dim1);
relNode(dim2);
end;
end;
end;
end;
assignParse := b;
end;
function ifParse(st: statementp): boolean;
var b: boolean;
begin (* if statement *)
b := false;
with st↑ do
begin
icond := checkarg(exprParse,svaltype); (* get the if condition *)
exprs := evalOrder(icond,nil,true);
getToken; (* look for the "then" *)
if (curToken.ttype <> reswdtype) or (curToken.rtype <> filtype) or
(curToken.filler <> thentype) then
begin
b := true; (* no good *)
pp20L('Expecting a "THEN" h',20); pp5('ere. ',4); ppFlush;
errprnt;
relExpr(st↑.icond);
end
else
with curToken do
begin
st↑.thn := stmntParse; (* get the then clause *)
st↑.thn↑.last := st; (* set up a back pointer *)
appendEnd(st,st↑.thn);
getToken; (* look for the "else" *)
if (ttype = delimtype) and (ch = ';') then
begin
semiseen := true;
getToken; (* peek past the ";" *)
end;
if (ttype = reswdtype) and (rtype = filtype) and
(filler = elsetype) then
begin
if semiseen then
begin
pp20L('Deleting extraneous ',20); pp20('";" before "ELSE" ',17);
errprnt;
end;
st↑.els := stmntParse;
st↑.els↑.last := st; (* set up a back pointer *)
st↑.els↑.next := st↑.thn↑.next; (* and one to the END *)
end
else begin backup := true; st↑.els := nil end;
end;
end;
ifParse := b;
end;
function forParse(st: statementp): boolean;
var b: boolean; lexp,dim1,dim2: nodep;
begin (* for statement *)
b := false;
dim1 := nil;
dim2 := nil;
with st↑ do
begin
forvar := checkarg(exprParse,svaltype); (* get the for variable *)
initial := nil;
step := nil;
final := nil;
with forvar↑ do (* make sure it's a variable *)
if not (((ntype = leafnode) and (ltype = varitype)) or
((ntype = exprnode) and (op = arefop))) then
begin (* no good *)
b := true;
pp20L('Need a scalar variab',20); pp10('le here. ',8); ppFlush;
errprnt;
end
else
begin
dim1 := getdim(forvar,dim1);
getToken; (* look for the ":=" *)
if (curToken.ttype <> reswdtype) or (curToken.rtype <> stmnttype) or
(curToken.stmnt <> assigntype) then
begin
b := true; (* no good *)
pp20L('Expecting a ":=" her',20); pp5('e. ',2); ppFlush;
errprnt;
end
else
begin (* so far so good *)
initial := checkarg(exprParse,svaltype); (* get the initial value *)
if not matchdim(dim1,getdim(initial,dim2),dimCheck) then
begin
pp20L('Dimensions don''t mat',20); pp20('ch in FOR statement ',19);
errprnt;
end;
getToken; (* look for the "STEP" *)
if (curToken.ttype <> reswdtype) or (curToken.rtype <> filtype) or
(curToken.filler <> steptype) then
begin
b := true; (* no good *)
pp20L('Expecting a "STEP" h',20); pp5('ere. ',4); ppFlush;
errprnt;
end
else
begin (* still good *)
step := checkarg(exprParse,svaltype); (* get the step value *)
if not matchdim(dim1,getdim(step,dim2),dimCheck) then
begin
pp20L('Dimensions don''t mat',20); pp20('ch in FOR statement ',19);
errprnt;
end;
getToken; (* look for the "TO" *)
if (curToken.ttype <> reswdtype) or (curToken.rtype <> filtype) or
(curToken.filler <> untltype) then
begin
b := true; (* no good *)
pp20L('Expecting an "UNTIL"',20); pp10(' here. ',6); ppFlush;
errprnt;
end
else
begin (* almost got it *)
final := checkarg(exprParse,svaltype); (* get the final value *)
if not matchdim(dim1,getdim(final,dim2),dimCheck) then
begin
pp20L('Dimensions don''t mat',20); pp20('ch in FOR statement ',19);
errprnt;
end;
with forvar↑ do
if ntype = leafnode then lexp := nil
else lexp := evalOrder(arg2,nil,true); (* push array subscripts *)
lexp := evalOrder(initial,lexp,true);
lexp := evalOrder(step,lexp,true);
exprs := evalOrder(final,lexp,true);
getToken; (* look for the "do" *)
if (curToken.ttype <> reswdtype) or (curToken.rtype <> filtype) or
(curToken.filler <> dotype) then
begin
b := true; (* no good *)
pp20L('Expecting a "DO" her',20); pp5('e. ',2); ppFlush;
errprnt;
end
else
begin
fbody := stmntParse; (* finally - get the body of the for *)
fbody↑.last := st; (* set up a back pointer *)
appendEnd(st,fbody);
end;
end;
end;
end;
end;
if dim1 <> nil then relNode(dim1);
if dim2 <> nil then relNode(dim2);
if b then (* bad statement - clean up a bit *)
begin
relExpr(forvar);
if initial <> nil then relExpr(initial);
if step <> nil then relExpr(step);
if final <> nil then relExpr(final);
backup := true;
end;
end;
forParse := b;
end;
function whileParse(st: statementp): boolean;
var b: boolean;
begin (* while statement *)
b := false;
with st↑ do
begin
cond := checkarg(exprParse,svaltype); (* get the while condition *)
exprs := evalOrder(cond,nil,true);
getToken; (* look for the "do" *)
if (curToken.ttype <> reswdtype) or (curToken.rtype <> filtype) or
(curToken.filler <> dotype) then
begin
b := true; (* no good *)
pp20L('Expecting a "DO" her',20); pp5('e. ',2); ppFlush;
errprnt;
relExpr(st↑.cond);
end
else
begin
st↑.body := stmntParse; (* get the body of the while *)
st↑.body↑.last := st; (* set up a back pointer *)
appendEnd(st,st↑.body);
end;
end;
whileParse := b;
end;
function untilParse(st: statementp): boolean;
var b: boolean;
begin (* until statement *)
st↑.stype := untiltype;
b := false;
st↑.body := stmntParse; (* get the body of the until *)
st↑.body↑.last := st; (* set up a back pointer *)
appendEnd(st,st↑.body);
getToken; (* look for the "until" *)
if (curToken.ttype = delimtype) and (curToken.ch = ';') then
begin
semiseen := true;
getToken; (* peek past the ";" *)
end;
if (curToken.ttype <> reswdtype) or (curToken.rtype <> filtype) or
(curToken.filler <> untltype) then
begin
b := true; (* no good *)
pp20L('Expecting an "UNTIL"',20); pp10(' here. ',6); ppFlush;
errprnt;
freeStatement(st↑.body); (* reclaim the body of the until *)
end
else
with st↑ do
begin
if semiseen then
begin
pp20L('Deleting extraneous ',20); pp20('";" before "UNTIL" ',18);
errprnt;
end;
cond := checkarg(exprParse,svaltype); (* get the until condition *)
exprs := evalOrder(cond,nil,true);
end;
untilParse := b;
end;
function caseParse(st: statementp): boolean;
var b,numcase,done: boolean; i,maxrange: integer;
co,cp,cn: nodep; endp: statementp;
procedure addClistnode(i: integer; sp: boolean);
var cln: nodep;
begin
cln := newNode;
with cln↑ do
begin
ntype := clistnode;
next := nil;
cval := i;
if sp then
begin
stmnt := stmntParse;
if semiseen then semiseen := false
else
begin (* look for the separating ";" *)
getToken;
with curToken do
if (ttype <> delimtype) or (ch <> ';') then (* not there *)
begin
backup := true;
if (ttype <> reswdtype) or (rtype <> stmnttype) or
(stmnt <> endtype) then
begin
pp20L('Inserting missing se',20); pp10('micolon ',7);
errprnt;
end;
end;
end;
stmnt↑.last := st; (* set up a back pointer *)
stmnt↑.next := endp; (* & a pointer to the end *)
while co <> nil do
begin
co↑.stmnt := stmnt; (* multiple labels for this statement *)
co := co↑.next;
end;
st↑.ncases := st↑.ncases + 1;
end
else
begin
stmnt := nil;
if co = nil then co := cln; (* need to fill in stmnt ptr later *)
end;
if cp = nil then begin st↑.caselist := cln; clast := nil end
else begin cp↑.next := cln; clast := cp end;
end;
cp := cln;
end;
begin (* case statement *)
b := false;
with curToken, st↑ do
begin
index := checkarg(exprParse,svaltype); (* get the case index *)
caselist := nil;
getToken; (* look for the "of" *)
if (ttype <> reswdtype) or (rtype <> filtype) or (filler <> oftype) then
begin
b := true; (* no good *)
pp20L('Expecting an "OF" he',20); pp5('re. ',3); ppFlush;
errprnt;
relExpr(st↑.index);
end
else
begin
getToken; (* look for the "begin" *)
if (ttype <> reswdtype) or (rtype <> stmnttype) or (stmnt <> blocktype) then
begin (* no good *)
backup := true;
pp20L('Expecting a "BEGIN" ',20); pp5('here.',5);
errprnt;
end;
(* *** ??? maybe should allow a block id here ??? *** *)
appendEnd(st,st); (* get an END statement *)
endp := next;
next := nil;
getToken; (* see what type of case we have *)
backup := true;
if ((ttype = delimtype) and (ch = '[')) or
((ttype = reswdtype) and (rtype = filtype) and (filler = elsetype)) then
numcase := true (* it's a numbered case statement *)
else numcase := false; (* regular type *)
maxrange := 0;
co := nil;
cp := nil;
done := false;
repeat
getToken;
if (ttype = reswdtype) and (rtype = stmnttype) and (stmnt = endtype) then
done := true
else if numcase then (* numbered case statement *)
if (ttype = delimtype) and (ch = '[') then
begin
cn := checkarg(exprParse,svaltype); (* get constant value *)
if cn↑.ntype <> leafnode then
begin
pp20L('Must have a constant',20); pp5(' here',5);
errprnt;
i := -2;
end
else i := round(cn↑.s);
if i > maxrange then maxrange := i;
relExpr(cn);
getDelim(']'); (* get closing ']' *)
getToken; (* peek ahead now *)
backup := true;
if ((ttype = delimtype) and (ch = '[')) or
((ttype = reswdtype) and (rtype = filtype) and
(filler = elsetype)) then addClistnode(i,false)
else addClistnode(i,true)
end
else if (ttype = reswdtype) and (rtype = filtype) and
(filler = elsetype) then addClistnode(-1,true)
else
begin
pp20L('Need a case number h',20); pp5('ere. ',4);
errprnt;
backup := true;
addClistnode(-2,true); (* use a garbage one & clean up *)
end
else (* regular case statement *)
begin
if (ttype <> delimtype) or (ch <> ';') then
begin
backup := true;
addClistnode(maxrange,true);
end;
maxrange := maxrange + 1;
end
until done;
if numcase then range := -maxrange else range := maxrange - 1;
exprs := evalOrder(index,nil,true);
end;
(* *** ??? block id with the end too ??? *** *)
end;
caseParse := b;
end;
function returnParse(st: statementp): boolean;
var b: boolean; d: datatypes; dim1,dim2: nodep;
begin (* return statement *)
getToken;
b := (curProc = nil) or inCoblock or (curCmon <> nil); (* return ok here? *)
if b then
begin
pp20L('Can''t have a RETURN ',20); pp20('statement here. ',15);
ppFlush;
errprnt;
backup := true;
end
else
with curToken do
begin
st↑.rproc := curProc↑.p;
d := curProc↑.vtype;
if (ttype = delimtype) and (ch = '(') then (* returning a result? *)
begin
if d <> nulltype then
begin
st↑.retval := checkarg(exprParse,d);
dim1 := nil; (* now check that dimensions match *)
dim2 := nil;
if not matchdim(getdim(curProc↑.p,dim1),getdim(st↑.retval,dim2),dimCheck) then
begin
pp20L('Returning result of ',20); pp20('wrong dimension ',15);
errprnt;
end;
relNode(dim1);
relNode(dim2);
end
else
begin
st↑.retval := exprParse;
if st↑.retval <> nil then
begin
pp20L('Procedure doesn''t re',20); pp20('turn result! ',12);
errprnt;
end;
end;
getDelim(')'); (* look for closing ")" *)
end
else
begin
backup := true;
st↑.retval := nil;
if d <> nulltype then
begin
pp20L('Need a value to retu',20); pp10('rn with ',7);
errprnt;
end
end;
with st↑ do
if retval <> nil then exprs := evalOrder(retval,nil,true);
end;
returnParse := b;
end;
function affixParse(st: statementp): boolean;
var opt,b: boolean; lexp: nodep;
begin (* affix statement *)
b := false;
opt := true;
with st↑, curToken do
begin
frame1 := checkarg(exprParse,frametype); (* get the first frame *)
frame2 := nil;
byvar := nil;
atexp := nil;
rigid := true; (* default flavor affixment *)
with frame1↑ do (* make sure it's a variable *)
begin
b := ((ntype <> leafnode) or (ltype <> varitype));
if b then b := ((ntype <> exprnode) or (op <> arefop));
end;
if b then
begin (* no good *)
pp20L('Need a frame variabl',20); pp10('e here. ',7); ppFlush;
end
else
begin
getToken; (* look for the "to" *)
if (ttype <> reswdtype) or (rtype <> filtype) or (filler <> totype) then
begin
b := true; (* no good *)
pp20L('Expecting a "TO" her',20); pp5('e. ',2); ppFlush;
end
else
begin (* so far so good *)
frame2 := checkarg(exprParse,frametype); (* get the other frame *)
with frame2↑ do (* make sure it's a variable *)
begin
b := ((ntype <> leafnode) or (ltype <> varitype));
if b then b := ((ntype <> exprnode) or (op <> arefop));
end;
if b then
begin (* no good *)
pp20L('Need a frame variabl',20); pp10('e here. ',7); ppFlush;
end
else
while opt and not b do
begin (* now look for optional parts: AT, BY & how *)
getToken;
if (ttype = reswdtype) and (rtype = filtype) and (filler = bytype) then
begin
byvar := checkarg(exprParse,transtype); (* get the BY var *)
checkdim(byvar,distancedim↑.dim);
with byvar↑ do (* make sure it's a variable *)
begin
b := ((ntype <> leafnode) or (ltype <> varitype));
if b then b := ((ntype <> exprnode) or (op <> arefop));
end;
if b then
begin (* no good *)
pp20L('Need a trans variabl',20); pp10('e here. ',7); ppFlush;
end
end
else if (ttype = reswdtype) and (rtype = filtype) and
(filler = attype) then
begin
atexp := checkarg(exprParse,transtype); (* get the AT expression *)
checkdim(atexp,distancedim↑.dim);
end
else if (ttype = reswdtype) and (rtype = filtype) and
(filler = rigidlytype) then rigid := true
else if (ttype = reswdtype) and (rtype = filtype) and
(filler = nonrigidlytype) then rigid := false
else
begin opt := false; backup := true end;
end;
with frame1↑ do
if ntype = leafnode then lexp := nil
else lexp := evalOrder(arg2,nil,true); (* push array subscripts *)
with frame2↑ do
if ntype <> leafnode then lexp := evalOrder(arg2,lexp,true);
if byvar <> nil then
with byvar↑ do
if ntype <> leafnode then lexp := evalOrder(arg2,lexp,true);
if atexp <> nil then exprs := evalOrder(atexp,lexp,true)
else exprs := lexp;
end;
end;
if b then (* bad statement - clean up a bit *)
begin
relExpr(frame1);
if frame2 <> nil then relExpr(frame2);
if byvar <> nil then relExpr(byvar);
if atexp <> nil then relExpr(atexp);
errprnt;
backup := true;
end;
end;
affixParse := b;
end;
function unfixParse(st: statementp): boolean;
var b: boolean; lexp: nodep;
begin (* unfix statement *)
b := false;
with st↑, curToken do
begin
frame1 := checkarg(exprParse,frametype); (* get the first frame *)
frame2 := nil;
byvar := nil;
atexp := nil;
with frame1↑ do (* make sure it's a variable *)
begin
b := ((ntype <> leafnode) or (ltype <> varitype));
if b then b := ((ntype <> exprnode) or (op <> arefop));
end;
if b then
begin (* no good *)
pp20L('Need a frame variabl',20); pp10('e here. ',7); ppFlush;
end
else
begin
getToken; (* look for the "from" *)
if (ttype <> reswdtype) or (rtype <> filtype) or
(filler <> fromtype) then
begin
b := true; (* no good *)
pp20L('Expecting a "FROM" h',20); pp5('ere. ',4); ppFlush;
end
else
begin (* so far so good *)
frame2 := checkarg(exprParse,frametype); (* get the other frame *)
with frame2↑ do (* make sure it's a variable *)
begin
b := ((ntype <> leafnode) or (ltype <> varitype));
if b then b := ((ntype <> exprnode) or (op <> arefop));
end;
if b then
begin (* no good *)
pp20L('Need a frame variabl',20); pp10('e here. ',7);ppFlush;
end
else
begin
with frame1↑ do
if ntype = leafnode then lexp := nil
else lexp := evalOrder(arg2,nil,true); (* push array subscripts *)
with frame2↑ do
if ntype <> leafnode then exprs := evalOrder(arg2,lexp,true)
else exprs := lexp;
end;
end;
end;
if b then (* bad statement - clean up a bit *)
begin
relExpr(frame1);
if frame2 <> nil then relExpr(frame2);
errprnt;
backup := true;
end;
end;
unfixParse := b;
end;
function signalParse(st: statementp): boolean;
var b: boolean;
begin (* signal & wait statements *)
b := false;
with st↑ do
begin
event := checkarg(exprParse,eventtype); (* get the event to use *)
with event↑ do (* make sure it's a variable *)
b := not (((ntype = leafnode) and (ltype = varitype)) or
((ntype = exprnode) and (op = arefop)));
if b then
begin (* no good *)
pp20L('Need an event variab',20); pp10('le here. ',8); ppFlush;
errprnt;
backup := true;
relExpr(event);
end
else
with event↑ do
if ntype <> leafnode then exprs := evalOrder(arg2,nil,true);
end;
signalParse := b;
end;
function pauseParse(st: statementp): boolean;
var b: boolean;
begin (* pause statement *)
b := false;
with st↑ do
begin
ptime := exprParse; (* get pause time *)
if ptime = nil then
begin
b := true;
pp20L('Must specify how lon',20); pp20('g to pause. ',11); ppFlush;
errprnt;
end
else
begin
ptime := checkarg(ptime,svaltype); (* make sure it's of right type *)
checkdim(ptime,timedim↑.dim); (* and right dimension *)
exprs := evalOrder(ptime,nil,true);
end;
end;
pauseParse := b;
end;
function printParse(st: statementp): boolean;
var b: boolean;
begin (* print, prompt & abort statements *)
b := false;
with st↑ do
begin
pnode↑.arg2 := nil;
getargs(pnode); (* pretend we just saw a queryop *)
plist := pnode↑.arg2; (* store away pointer to print list *)
if plist <> nil then exprs := evalOrder(plist,nil,false)
else if stype = printtype then
begin
b := true;
pp20L('PRINT must have some',20); pp20('thing to print. ',15); ppFlush;
errprnt;
end;
debugLev := 0; (* for abort *)
end;
printParse := b;
end;
(* aux functions for motion clauses: thencode & clauseParse *)
function thencode(evp: boolean): statementp;
var s, st: statementp; n: nodep; v: varidefp;
begin
s := stmntParse; (* get THEN code *)
if s↑.stype = signaltype then st := s (* treat signal specially *)
else
begin
st := newStatement;
with st↑ do (* make a cmon to execute the code *)
begin
stype := cmtype;
deferCm := false;
exprCm := false;
conclusion := s;
appendEnd(st,s);
n := newNode;
oncond := n;
end;
v := makeNewVar(cmontype,nil); (* make a variable for the cmon *)
v↑.s := st;
st↑.cdef := v;
if evp then (* do we need to make an event variable? *)
begin
with n↑ do
begin
ntype := leafnode;
ltype := varitype;
vari := makeNewVar(eventtype,nil);
vid := nil;
end;
end;
end;
thencode := st;
end;
function clauseParse(absSeen: boolean): nodep;
var cl,nv,vdim: nodep; b: boolean; dummyrel: reltypes; bits,i: integer; d: datatypes;
function relParse: reltypes;
begin
getToken; (* get the relation *)
with curToken do
if (ttype = reswdtype) and (rtype = optype) and (op <= sgtop) then
relParse := op
else
begin
pp20L('Need a relational op',20); pp20('erator here ',11);
errprnt;
backup := true;
relParse := seqop;
end;
end;
begin
getToken;
with curToken do
begin
if (ttype = identtype) then b := id↑.name↑.ch = 'SPEED_FACT' else b := false;
if b then
begin
cl := newNode;
with cl↑ do
begin
ntype := sfacnode;
dummyrel := relParse; (* skip over the "=" *)
clval := checkarg(exprParse,svaltype);
checkdim(clval,nodim↑.dim);
end;
end
else if (ttype <> reswdtype) or (rtype <> clsetype) then
begin
cl := nil;
backup := true;
pp20L('Not a valid clause ',18);
errprnt;
end
else
begin
cl := newNode;
with cl↑ do
case clause of
durationtype:
begin
ntype := durnode;
durrel := relParse;
durval := checkarg(exprParse,svaltype);
checkdim(durval,timedim↑.dim);
end;
wobbletype,
stopwaittimetype:
begin
if clause = wobbletype then
begin
ntype := wobblenode;
vdim := angledim↑.dim;
end
else
begin
ntype := swtnode;
vdim := timedim↑.dim;
end;
dummyrel := relParse;
clval := checkarg(exprParse,svaltype);
checkdim(clval,vdim);
end;
nullingtype,
nonullingtype:
begin
ntype := nullingnode;
if clause = nonullingtype then notp := true else notp := false;
end;
cwtype,
ccwtype:
begin
ntype := cwnode;
if clause = cwtype then notp := false else notp := true;
end;
approachtype,
departuretype:
begin
if clause = approachtype then ntype := apprnode else ntype := deprnode;
dummyrel := relParse;
getToken; (* check for NILDEPROACH *)
if (ttype = reswdtype) and
(rtype = clsetype) and (clause = nildeproachtype) then loc := nil
else
begin (* need to get deproach value *)
backup := true;
loc := exprParse; (* can be scalar, vector or trans *)
checkdim(loc,distancedim↑.dim);
end;
getToken; (* now look for THEN *)
if (ttype = reswdtype) and
(rtype = filtype) and (filler = thentype) then
begin
code := thencode(true);
end
else
begin code := nil; backup := true; end;
end;
forcewristtype:
begin
ntype := wristnode;
getToken;
if (ttype = reswdtype) and (rtype = optype) and
(curToken.op = notop) then
begin
notp := true;
getToken;
end
else notp := false;
if (ttype <> reswdtype) or (rtype <> filtype) or
(filler <> zeroedtype) then
begin
backup := true;
pp20L('Garbage clause ',14);
errprnt;
end
end;
forceframetype:
begin
ntype := ffnode;
if not absSeen then dummyrel := relParse;
ff := checkarg(exprParse,transtype);
checkdim(ff,distancedim↑.dim);
csys := true; (* assume WORLD if not specified *)
getToken;
if (ttype = reswdtype) and (rtype = filtype) and (filler = intype) then
begin (* see whether WORLD or HAND coord sys *)
getToken;
if (ttype = reswdtype) and (rtype = filtype) and
(filler = handtype) then csys := false (* use HAND coords *)
else if (ttype <> reswdtype) or (rtype <> filtype) or
(filler <> worldtype) then (* better be WORLD coords *)
begin
backup := true;
pp20L('Need HAND or WORLD h',20); pp5('ere ',3);
errprnt;
end
end
else backup := true;
end;
forcetype,
torquetype,
angularvelocitytype:
begin
ntype := forcenode;
if clause = forcetype then
begin ftype := force; vdim := forcedim↑.dim end
else if clause = torquetype then
begin ftype := torque; vdim := torquedim↑.dim end
else begin ftype := angvelocity; vdim := angveldim↑.dim end;
if absSeen then ftype := succ(ftype);
getToken;
if (ttype = delimtype) and (ch = '(') then (* short form *)
begin
b := true;
fvec := checkarg(exprParse,vectype);
getDelim(')'); (* get closing ")" *)
getToken;
end
else b := false; (* long form *)
if absSeen then
begin
if (ttype <> reswdtype) or (rtype <> optype) or
(curToken.op <> absop) then
begin
backup := true;
pp20L('Need closing "|" her',20); ppChar('e');
errprnt;
end;
end
else backup := true;
frel := relparse;
fval := checkarg(exprParse,svaltype);
checkdim(fval,vdim);
with curMotion↑ do
if (stype = opentype) or (stype = closetype) or (stype = operatetype) then
begin
b := true; (* so we don't look for a vector specification *)
cl↑.fvec := nil;
end;
if not b then
begin
getToken;
if (ttype <> reswdtype) or (rtype <> filtype) or
((filler <> abouttype) and (filler <> alongtype)) then
begin
backup := true;
pp20L('Need ALONG or ABOUT ',20); pp5('here ',4);
errprnt;
end;
fvec := checkarg(exprParse,vectype);
end;
getToken; (* check for force frame *)
backup := true;
if (ttype = reswdtype) and (rtype = filtype) and (filler = oftype) then
begin
rtype := clsetype; (* make curToken look like forceframe clause *)
clause := forceframetype;
fframe := clauseParse(true);
end
else fframe := nil;
end;
stiffnesstype:
begin
ntype := stiffnode;
dummyrel := relParse; (* skip over the "=" *)
getDelim('('); (* now look for the "(" *)
fv := exprParse; (* get the first stiffness component *)
if getDtype(fv) = svaltype then (* see if it's 6 scalars or 2 vectors *)
for i := 1 to 2 do
begin
nv := newNode;
with nv↑ do
begin
ntype := exprnode;
op := vmakeop;
if i = 2 then arg1 := checkarg(exprParse,svaltype) else arg1 := cl↑.fv;
getDelim(',');
arg2 := checkarg(exprParse,svaltype);
getDelim(',');
arg3 := checkarg(exprParse,svaltype);
end;
if i = 1 then begin fv := nv; getDelim(',') end else mv := nv;
end
else
begin (* two vectors *)
fv := checkarg(fv,vectype);
getDelim(','); (* now look for the separating "," *)
mv := checkarg(exprParse,vectype);
end;
checkdim(fv,fvstiffdim);
checkdim(mv,mvstiffdim);
getDelim(')'); (* now look for the ")" *)
getToken; (* is a center of compliance given? *)
if (ttype = reswdtype) and (rtype = filtype) and (filler = abouttype) then
coc := checkarg(exprParse,transtype)
else begin coc := nil; backup := true; end;
end;
gathertype:
begin
ntype := gathernode;
dummyrel := relParse; (* skip over the "=" *)
getDelim('('); (* now look for the "(" *)
b := false;
gbits := 0;
repeat
bits := 0;
getToken; (* get component to gather *)
if (ttype <> reswdtype) or (rtype <> clsetype) then b := true
else
case clause of
fxtype: bits := 1B;
fytype: bits := 2B;
fztype: bits := 4B;
mxtype: bits := 10B;
mytype: bits := 20B;
mztype: bits := 40B;
t1type: bits := 100B;
t2type: bits := 200B;
t3type: bits := 400B;
t4type: bits := 1000B;
t5type: bits := 2000B;
t6type: bits := 4000B;
tbltype: bits := 10000B;
end;
if bits = 0 then b := true; (* bad clause *)
gbits := gbits + bits; (* really need to logically or these *)
if b then
begin
pp20L('Expecting a force co',20); pp20('mponent here ',12);
errprnt;
end
else getToken; (* pick up the "," or ")" *)
until (ttype <> delimtype) or (ch <> ',') or b;
backup := true;
getDelim(')'); (* now look for the ")" *)
end;
end;
end;
end;
clauseParse := cl;
end;
function cmonParse(st: statementp; deferred: boolean): boolean;
var b, oldInMove: boolean; i: integer; t: tokenp;
oldCmon, oldErrHandler: statementp; v: varidefp;
procedure notInMove;
begin
b := true;
pp20L(' must be part of a M',20); pp20('OVE statement - will',20);
pp20(' flush cmon. ',12);
errprnt;
st↑.oncond := nil;
end;
begin (* cmon statement *)
b := false;
oldCmon := curCmon;
curCmon := st;
oldErrHandler := curErrHandler;
with st↑, curToken do
begin
deferCm := deferred; (* remember if we are deferred or not *)
exprCm := false;
oncond := nil;
getToken; (* see what sort of cmon we have *)
if (ttype = reswdtype) and (rtype = clsetype) then
begin
if (clause = durationtype) or (clause = forcetype) or (clause = torquetype) then
begin
backup := true;
oncond := clauseParse(false);
end
else if (clause = arrivaltype) or (clause = departingtype) then
begin
if inMove then (* only valid within a motion *)
begin
oncond := newNode;
with oncond↑ do
if clause = departingtype then ntype := departingnode
else
begin
ntype := arrivalnode;
evar := makeNewVar(eventtype,nil);
end
end
else
begin
pp20L('Arrival/departing ',17);
notInMove;
end;
end
else if clause = errortype then
begin
oncond := newNode;
with oncond↑ do
begin
ntype := errornode;
getToken; (* skip over the "=" *)
eexpr := exprParse; (* get desired error bits *)
checkdim(eexpr,nodim↑.dim);
end;
if inMove then curErrHandler := st
else
begin (* no good *)
relExpr(oncond↑.eexpr);
relNode(oncond);
pp20L('Error handler ',13);
notInMove;
end;
end
else
begin
b := true; (* no good *)
backup := true;
pp20L('Unknown ON condition',20); pp10(' test. ',6); ppFlush;
errprnt;
relExpr(clauseParse(false)); (* try to parse it anyway *)
end
end
else if (ttype = reswdtype) and (rtype = optype) and (op = absop) then
begin (* is it |Force...| or |Torque...|? *)
getToken; (* see what next token is *)
backup := true;
if (ttype = reswdtype) and (rtype = clsetype) and
((clause = forcetype) or (clause = torquetype)) then
oncond := clauseParse(true) (* yes - |Force/Torque...| cmon *)
else
begin (* no - expression cmon *)
exprCm := true;
t := copyToken; (* make a copy of token we just peeked at *)
next := t; (* fix things up so the peeked at token is next *)
ttype := reswdtype; (* and the "|" gets seen again by exprParse *)
rtype := optype;
op := absop;
if macrodepth = 0 then (* pretend we're a macro *)
begin
macrodepth := 1;
curmacstack[macrodepth] := nil;
macrostack[macrodepth] := nil;
end;
oncond := exprParse; (* get expression for cmon *)
relToken(t); (* done with peeked at token now *)
end
end
else
begin
backup := true;
oncond := exprParse; (* get the cmon condition *)
if getdtype(oncond) <> eventtype then exprCm := true;
end;
if oncond <> nil then
with oncond↑ do
if (ntype = forcenode) and not inMove then
begin
relExpr(oncond);
pp20L('Force sensing ',13);
notInMove;
end
else if exprCm or (ntype = durnode) or (ntype = forcenode) then
exprs := evalOrder(oncond,nil,true)
else if ntype = exprnode then (* subscripted event *)
exprs := evalOrder(arg2,nil,true)
else exprs := nil;
getToken; (* look for the "do" *)
if (ttype <> reswdtype) or (rtype <> filtype) or (filler <> dotype) then
begin
b := true; (* no good *)
pp20L('Expecting a "DO" her',20); pp5('e. ',2); ppFlush;
errprnt;
relExpr(oncond);
end
else
begin
oldInMove := inMove;
InMove := false;
conclusion := stmntParse; (* get the body of the cmon *)
appendEnd(st,conclusion);
InMove := oldInMove;
end;
end;
v := makeNewVar(cmontype,nil);
v↑.s := st;
st↑.cdef := v;
(* *** check if cmon has a label & if so mark label as labelling a cmon *** *)
curCmon := oldCmon;
curErrHandler := oldErrHandler;
cmonParse := b;
end;
function enableParse(st: statementp): boolean;
var b: boolean; v: varidefp;
begin (* enable & disable statements *)
b := false;
st↑.cmonlab := nil;
with curToken do
begin
getToken; (* get the label of the cmon to enable/disable *)
if ttype = identtype then (* check that it's really a label *)
begin
v := varLookup(id);
if v = nil then (* need to define it *)
begin
v := makeUVar(labeltype,id);
st↑.cmonlab := v;
pp20L('Undeclared identifie',20); pp20('r defined to be a la',20);
pp5('bel. ',4);
errprnt;
end
else if v↑.vtype = labeltype then st↑.cmonlab := v (* ok *)
else b := true (* no good *)
end
else
begin
backup := true;
if curCmon = nil then b := true; (* no good, unless in a cmon body *)
end;
end;
if b then
begin (* no good *)
pp20L('Need a label here. ',18); ppFlush;
errprnt;
end;
enableParse := b;
end;
function moveParse(st: statementp): boolean;
var b, done, vp, oldInMove, movep, operatep, centerp, openp, arrp: boolean;
lastclause, cl, lexpr: nodep;
via, dest, appr, depr, wobble, sfac, dur, vel, torquecl: nodep;
oldmotion, lastcmon: statementp;
clab: varidefp; oldMoveLevel, useForce, cmForce: integer;
gathering, zwrist, notaxis: boolean; stiff, ffr, fn1: nodep;
procedure addClause(cl: nodep);
begin
if cl <> nil then (* make sure it was ok *)
begin
if lastclause <> nil then (* add it to clause list *)
lastclause↑.next := cl
else st↑.clauses := cl; (* first clause seen *)
lastclause := cl;
cl↑.next := nil;
end;
end;
procedure ffcompare(ff2: nodep);
var b: boolean; v1,v2: varidefp;
begin
if ff2 <> nil then
if ffr = nil then ffr := ff2 (* remember first force frame we see *)
else
begin (* see if they match *)
b := ffr↑.csys = ff2↑.csys; (* make sure they use same coord sys *)
v1 := nil;
v2 := nil;
with ffr↑.ff↑ do
if ntype = leafnode then
if ltype = pconstype then v1 := cname
else if ltype = varitype then v1 := vari else b := false
else if (ntype = exprnode) and (op = arefop) then v1 := arg1↑.vari
else b := false;
with ff2↑.ff↑ do
if ntype = leafnode then
if ltype = pconstype then v2 := cname
else if ltype = varitype then v2 := vari else b := false
else if (ntype = exprnode) and (op = arefop) then v2 := arg1↑.vari
else b := false;
if not (b or (v1 = v2)) then
begin
pp20L('MOVE statement has m',20); pp20('ultiply defined forc',20);
pp10('e frames ',8);
errprnt;
end;
end;
end;
procedure fcheck(fn: nodep); (* check force axis is ok *)
var vec: vectorp;
procedure badvector(fn: nodep); (* axis error *)
var bad: nodep;
begin
pp20L('force direction must',20); pp20(' be along an axis - ',20);
pp20('assuming zhat ',13);
errprnt; (* not right line, but... *)
bad := newNode;
with bad↑ do
begin
ntype := exprnode;
op := badop;
arg1 := fn↑.fvec;
arg2 := newNode;
end;
with bad↑.arg2↑ do
begin ntype := leafnode; ltype := vectype; v := zhat end;
fn↑.fvec := bad;
end;
begin (* note: can't really check variables or expressions *)
ffcompare(fn↑.fframe); (* first check its force frame *)
if (useForce + cmForce > 1) and notaxis then
begin (* first force spec was bad - fix it now *)
pp20L('In previous force sp',20); pp20('ecification: ',12);
badvector(fn1);
end;
vec := nil;
with fn↑.fvec↑ do
if ntype = leafnode then vec := pcval↑.v (* first check if axis vector *)
else if op = vnegop then (* or negative axis vector *)
if arg1↑.ntype = leafnode then vec := arg1↑.pcval↑.v;
if not((vec = xhat) or (vec = yhat) or (vec = zhat)) then
if useForce + cmForce = 1 then
begin (* single sense/apply *)
fn1 := fn;
notaxis := true; (* remember that it's not along an axis *)
end
else badvector(fn); (* multiple axes - error *)
end;
procedure addCmon(clab: varidefp; deferred: boolean);
var cst: statementp; cl: nodep; bad: boolean;
begin
bad := false;
if (clab <> nil) or deferred then (* need to check for "ON" *)
begin
getToken;
with curToken do
if (ttype <> reswdtype) or (rtype <> stmnttype) or (stmnt <> cmtype) then
begin
bad := true;
backup := true;
pp20L('Expected an "ON" her',20); ppChar('e');
errprnt;
end;
end;
if not bad then
begin
cst := newStatement;
with cst↑ do
begin
stype := cmtype;
stlab := clab;
if clab <> nil then clab↑.s := cst; (* define the label *)
end;
bad := cmonParse(cst,deferred); (* if it's bad we should flush it, but... *)
with cst↑.oncond↑ do
if ntype = forcenode then
begin
cmForce := cmForce + 1;
if movep then fcheck(cst↑.oncond);
end
else if ntype = arrivalnode then
begin
if arrp then
begin
pp20L('Can only specify one',20); pp20(' "ON ARRIVAL DO" for',20);
pp10(' a motion!',10);
errprnt;
end;
arrp := true;
end;
cl := newNode;
with cl↑ do
begin
ntype := cmonnode;
cmon := cst;
errHandlerp := cst↑.oncond↑.ntype = errornode;
if errHandlerp then (* point back to motion statement, not cmon *)
cst↑.conclusion↑.next↑.bparent := st;
end;
addClause(cl);
end;
end;
begin (* move statement *)
b := false;
oldmotion := curMotion; (* remember previous motion statement *)
curMotion := st; (* remember this motion *)
oldInMove := inMove;
inMove := true;
oldMoveLevel := moveLevel; (* remember our lex level for retry *)
if curBlock <> nil then moveLevel := curBlock↑.level else moveLevel := 0;
arrp := false;
movep := false;
operatep := false;
centerp := false;
openp := false;
with st↑, curToken do
begin
if stype = movetype then movep := true
else if stype = operatetype then operatep := true
else if stype = centertype then centerp := true else openp := true;
if movep or centerp then
cf := checkarg(exprParse,frametype) (* what are we moving? *)
else cf := checkarg(exprParse,svaltype);
with cf↑ do (* make sure it's a variable *)
begin
b := (ntype <> leafnode) or (ltype <> varitype);
if b then b := (ntype <> exprnode) or (op <> arefop);
if not b then (* ok so far, check some more *)
if centerp then
begin (* check for arms *)
if ntype <> leafnode then b := true
else b := (vari↑.level <> 0) or not (vari↑.offset in [0,4,8,12]);
(* offsets: 0=barm, 4=yarm, 8=garm, 12=rarm *)
end
else if operatep then
begin (* check for driver *)
if ntype <> leafnode then b := true
else b := (vari↑.level <> 0) or (vari↑.offset <> 16);
(* offset: 16=driver *)
end
else if openp then
begin (* check for scalar devices *)
if ntype <> leafnode then b := true
else b := (vari↑.level <> 0) or not (vari↑.offset in [2,6,10,14,20]);
(* offsets: 2=bhand, 6=yhand, 10=ghand, 14=rhand, 20=vise *)
end;
end;
if b then
begin
pp20L('Need a device variab',20); pp10('le here. ',8); ppFlush;
errprnt;
end;
clauses := nil;
lastclause := nil;
lastcmon := nil;
dest := nil;
appr := nil;
depr := nil;
wobble := nil;
sfac := nil;
dur := nil;
useForce := 0;
cmForce := 0;
stiff := nil;
gathering := false;
ffr := nil;
fn1 := nil;
notaxis := false;
done := false;
repeat (* get all the clauses *)
flushcomments := false; (* comments are ok here *)
getToken;
flushcomments := true; (* but not inside other clauses *)
if (ttype = reswdtype) and (rtype = filtype) then (* TO, VIA, WITH *)
begin
if filler = totype then (* get destination *)
begin
if dest <> nil then
begin
pp20L('Can only specify one',20); pp20(' destination for a m',20);
pp10('otion! ',6);
errprnt;
end;
dest := newNode;
with dest↑ do
begin
ntype := destnode;
if movep then loc := checkarg(exprParse,transtype)
else loc := checkarg(exprParse,svaltype);
checkdim(loc,distancedim↑.dim);
code := nil;
end;
addClause(dest);
end
else if filler = viatype then (* get VIA point(s) *)
begin (* ** maybe should check that this is a MOVE stmnt ** *)
repeat
via := newNode;
addClause(via);
with via↑ do
begin
ntype := viaptnode;
vlist := ttype = delimtype; (* part of a via list or not *)
via := checkarg(exprParse,transtype);
checkdim(via,distancedim↑.dim);
velocity := nil;
duration := nil;
vcode := nil;
getToken;
if (ttype = reswdtype) and
(rtype = filtype) and (filler = wheretype) then
begin
vp := true;
while vp do
begin (* look for velocity & duration specs *)
getToken;
if (ttype = reswdtype) and
(rtype = clsetype) and (clause = velocitytype) then
begin
getToken; (* skip over the '=' *)
velocity := checkarg(exprParse,vectype);
checkdim(velocity,veldim↑.dim);
end
else if (ttype = reswdtype) and
(rtype = clsetype) and (clause = durationtype) then
begin
backup := true;
duration := clauseParse(false); (* go get the duration spec *)
end
else if (ttype <> delimtype) or (ch <> ',') then
begin backup := true; vp := false; end;
end;
end;
if (ttype = reswdtype) and
(rtype = filtype) and (filler = thentype) then
begin
backup := false;
vcode := thencode(true);
getToken;
end;
end
until (via↑.vcode <> nil) or (ttype <> delimtype) or (ch <> ',');
backup := true;
end
else if filler = withtype then (* get WITH clause *)
begin
addClause(clauseParse(false));
with lastclause↑ do
if ntype = gathernode then gathering := true
else if ntype = wristnode then zwrist := true
else if ntype = stiffnode then stiff := lastclause
else if ntype = wobblenode then wobble := lastclause
else if ntype = sfacnode then sfac := lastclause
else if ntype = durnode then dur := lastclause
else if (ntype = apprnode) and (loc <> nil) then appr := lastclause
else if (ntype = deprnode) and (loc <> nil) then depr := lastclause
else if ntype = ffnode then ffcompare(lastclause)
else if ntype = forcenode then
begin
useForce := useForce + 1;
if movep then fcheck(lastclause);
end;
end
else if filler = defertype then (* deferred cmon *)
begin
addCmon(nil,true);
end
else (* unknown clause or we're done *)
begin done := true; backup := true end
end
else if ttype = labeldeftype then
begin (* a label *)
clab := lab; (* remember it *)
getToken;
if (ttype = reswdtype) and (rtype = filtype) and (filler = defertype) then
addCmon(clab,true) (* labelled deferred cmon *)
else
begin
backup := true;
addCmon(clab,false); (* labelled cmon *)
end;
end
else if (ttype = reswdtype) and (rtype = stmnttype) and (stmnt = cmtype) then
begin
addCmon(nil,false); (* condition monitor *)
end
else if (ttype = reswdtype) and (rtype = clsetype) and
((clause = cwtype) or (clause = ccwtype)) then
begin
backup := true;
addClause(clauseParse(false));
end
else if ttype = comnttype then
begin
cl := newNode;
cl↑.ntype := commentnode;
cl↑.length := len; (* copy comment *)
cl↑.str := str;
addClause(cl);
end
else (* that's all for this MOVE *)
begin done := true; backup := true end
until done;
if dest = nil then
begin
(* if movep or (openp and (cf↑.vari↑.offset <= 6)) then *)
if movep then
begin
pp20L('Need a destination f',20); pp20('or motion statement!',20);
errprnt;
end
end;
if notaxis and (useForce + cmForce = 1) then
begin (* single sense/apply *)
if ffr <> nil then
begin
pp20L('Can''t specify a forc',20); pp20('e frame with a rando',20);
pp20('m force vector ',14);
errprnt;
end;
ffr := newNode; (* make up a new force frame *)
with ffr↑ do
begin
ntype := ffnode;
ff := newNode;
with ff↑ do
begin
ntype := exprnode;
op := vmkfrcop; (* need to compute force frame *)
arg1 := fn1↑.fvec;
arg2 := nil;
arg3 := nil;
end;
csys := true; (* use world coords *)
pdef := true;
end;
addClause(ffr);
end;
(* now set up those expressions that need to be evaluated for this motion *)
lexpr := nil;
if cf <> nil then (* evaluate control frame *)
if cf↑.ntype <> leafnode then
lexpr := evalOrder(cf↑.arg2,nil,true); (* push array subscripts *)
if (sfac <> nil) and (dest <> nil) then (* evaluate speed factor *)
lexpr := evalOrder(sfac↑.clval,lexpr,false);
if dur <> nil then (* evaluate global time duration *)
lexpr := evalOrder(dur↑.durval,lexpr,false);
if movep then
begin (* MOVE statement has extra clauses *)
if wobble <> nil then (* evaluate wobble *)
lexpr := evalOrder(wobble↑.clval,lexpr,false);
if ffr <> nil then (* evaluate force frame *)
lexpr := evalOrder(ffr↑.ff,lexpr,false);
if stiff <> nil then (* deal with stiffness *)
begin
lexpr := evalOrder(stiff↑.fv,lexpr,false); (* evaluate force vector *)
lexpr := evalOrder(stiff↑.mv,lexpr,false); (* evaluate torque vector *)
if stiff↑.coc <> nil then (* evaluate center of compliance *)
lexpr := evalOrder(stiff↑.coc,lexpr,false);
end;
cl := clauses;
while cl <> nil do (* run through clauses *)
begin
if cl↑.ntype = forcenode then (* evaluate bias force values *)
lexpr := evalOrder(cl↑.fval,lexpr,false);
cl := cl↑.next;
end;
if depr <> nil then (* evaluate departure *)
lexpr := evalOrder(depr,lexpr,false);
cl := clauses;
while cl <> nil do (* run through clauses *)
begin
if cl↑.ntype = viaptnode then (* evaluate via points *)
lexpr := evalOrder(cl,lexpr,false);
cl := cl↑.next;
end;
if appr <> nil then (* evaluate approach *)
lexpr := evalOrder(appr,lexpr,false);
end
else if operatep then
begin (* handle OPERATE *)
torquecl := nil;
vel := nil;
cl := clauses;
while cl <> nil do (* run through clauses *)
with cl↑ do
begin
if ntype = forcenode then
if ftype = torque then torquecl := cl
else if ftype = angvelocity then vel := cl;
cl := next;
end;
if vel <> nil then (* evaluate angular velocity *)
lexpr := evalOrder(vel↑.fval,lexpr,false);
if torquecl <> nil then (* evaluate torque *)
lexpr := evalOrder(torquecl↑.fval,lexpr,false);
end
else if openp then
begin (* handle OPEN/CLOSE *)
cl := clauses;
while cl <> nil do (* run through clauses *)
begin
if cl↑.ntype = swtnode then (* evaluate stop wait time for vise *)
begin
lexpr := evalOrder(cl↑.clval,lexpr,false);
cl := nil;
end
else cl := cl↑.next;
end;
end;
if dest <> nil then (* evaluate destination *)
lexpr := evalOrder(dest,lexpr,false);
cl := clauses;
while cl <> nil do (* run through clauses *)
with cl↑ do
begin
if (ntype = cmonnode) and errHandlerp then (* evaluate error conds *)
lexpr := evalOrder(cmon↑.oncond↑.eexpr,lexpr,false);
cl := next;
end;
exprs := lexpr;
end;
curMotion := oldmotion; (* restore previous motion statement *)
inMove := oldInMove;
moveLevel := oldMoveLevel;
moveParse := b;
end;
function stopParse(st: statementp): boolean;
var d: datatypes; b: boolean;
begin (* stop statement *)
with st↑ do
begin
cf := exprParse; (* what are we stopping? *)
if cf = nil then (* use default = cf of current motion (if any) *)
begin
if curMotion = nil then
begin
pp20L('Need to specify what',20); pp10(' to Stop ',8);
errprnt;
end
end
else
begin (* make sure it's a variable *)
d := getDtype(cf);
b := true;
with cf↑ do
if ((ntype = leafnode) and (ltype = varitype)) or
((ntype = exprnode) and (op = arefop)) then (* a variable? *)
if d = frametype then b := false (* assume any frame var is ok *)
else if (d = svaltype) and (ntype = leafnode) then
if (vari↑.level = 0) and (* check for scalar devices *)
(vari↑.offset in [2,6,10,14,16,20]) then b := false;
(* offsets: 2=bhand, 6=yhand, 10=ghand, 14=rhand, 16=driver, 20=vise *)
if b then
begin (* no good *)
pp20L('Need a device variab',20); pp10('le here. ',8);
errprnt;
relExpr(cf);
cf := nil;
end
end;
clauses := nil;
end;
stopParse := false; (* always ok *)
end;
function retryParse(st: statementp): boolean;
begin (* retry statement *)
if curErrhandler <> nil then
begin
st↑.rparent := curErrhandler;
st↑.rcode := curMotion;
st↑.olevel := moveLevel;
end
else
begin (* no good *)
st↑.rparent := nil;
st↑.rcode := nil;
pp20L('RETRY can only be in',20); pp20(' body of error handl',20); pp5('er. ',3);
errprnt;
end;
retryParse := false; (* always ok *)
end;
function wristParse(st: statementp): boolean;
var b: boolean; lexp: nodep;
begin (* wrist statement *)
b := false;
lexp := nil;
with st↑ do
begin
getDelim('('); (* get opening "(" *)
fvec := checkarg(exprParse,vectype);
checkdim(fvec,forcedim↑.dim);
with fvec↑ do (* make sure it's a variable *)
if (ntype = exprnode) and (op = arefop) then
lexp := evalorder(arg2,lexp,true) (* deal with subscripts *)
else if not ((ntype = leafnode) and (ltype = varitype)) then (* no good *)
begin
b := true;
pp20L('Need a variable here',20); ppChar('.'); ppFlush;
errprnt;
end;
getDelim(','); (* get separating "," *)
tvec := checkarg(exprParse,vectype);
checkdim(tvec,torquedim↑.dim);
with tvec↑ do (* make sure it's a variable *)
if (ntype = exprnode) and (op = arefop) then
lexp := evalorder(arg2,lexp,true) (* deal with subscripts *)
else if not ((ntype = leafnode) and (ltype = varitype)) then (* no good *)
begin
b := true;
pp20L('Need a variable here',20); ppChar('.'); ppFlush;
errprnt;
end;
getDelim(')'); (* get closing ")" *)
exprs := lexp;
end;
wristParse := b;
end;
function requireParse(st: statementp): boolean;
var b: boolean; chr: ascii; i,j: integer; s: strngp; n: nodep;
begin (* require statement *)
b := false;
n := nil;
with st↑, curToken do
begin
getToken; (* see what type of require we have *)
if (ttype = reswdtype) and (rtype = filtype) and (filler = errmodestype) then
begin
rfil := false;
getToken; (* get the error mode values *)
if ttype <> constype then b := true
else begin n := cons; if cons↑.ltype <> strngtype then b := true; end;
if b then
begin
backup := true;
pp20L('Expecting a string h',20); pp5('ere ',3);
errprnt;
end
else
begin
rfils := cons↑.str;
rfilen := cons↑.length;
j := 1;
s := rfils;
for i := 1 to rfilen do
begin
chr := upperCase(s↑.ch[j]);
if j < 10 then j := j + 1 else begin j := 1; s := s↑.next end;
if chr = 'F' then dimCheck := false; (* only mode we know about *)
end
end
end
else if (ttype = reswdtype) and (rtype = filtype) and
(filler = sourcefiletype) then
begin
rfil := true;
getToken; (* get the name of the file *)
if ttype <> constype then b := true
else begin n := cons; if cons↑.ltype <> strngtype then b := true; end;
if b then
begin
backup := true;
pp20L('Need a file name her',20); ppChar('e');
errprnt;
end
else
begin
rfils := cons↑.str;
rfilen := cons↑.length;
if filedepth < 5 then
begin
filedepth := filedepth + 1;
fileopen(rfilen,rfils);
getToken; (* now try to skip over the E directory *)
if (ttype = delimtype) and (ch = ';') then
begin
semiseen := true;
getToken;
end;
backup := true;
end
else
begin
pp20L('Can only nest files ',20); pp20('5 deep - ignoring re',20);
pp5('quire',5);
errprnt;
end
end;
end
else
begin
pp20L('Unknown require opti',20); pp5('on ',2);
errprnt;
b := true;
end;
if n <> nil then relNode(n);
end;
requireParse := b;
end;
function defineParse(st: statementp): boolean;
var oldExpandmacros,b: boolean; v,vp: varidefp; t,tp: tokenp;
begin (* define statement *)
b := false;
oldExpandmacros := expandmacros;
expandmacros := false;
with st↑, curToken do
begin
getToken; (* get the name of the macro *)
if ttype <> identtype then
begin
b := true;
pp20L('Need an identifier h',20); pp5('ere. ',5);
errprnt;
end
else
begin
v := makeNewVar(mactype,id);
v↑.mdef := st;
macname := v;
v := nil;
getToken;
if (ttype = delimtype) and (ch = '(') then (* get macro args *)
begin
repeat
getToken; (* get the parameter's name *)
if ttype <> identtype then
begin
b := true;
pp20L('Need an identifier h',20); pp5('ere. ',5);
errprnt;
backup := true;
end
else
begin
if v = nil then begin v := newVaridef; vp := v end
else begin vp↑.next := newVaridef; vp := vp↑.next end;
with vp↑ do begin vtype := macargtype; name := id; end;
end;
getToken;
until b or (ttype <> delimtype) or (ch <> ',');
vp↑.next := nil;
backup := true;
getDelim(')'); (* get closing ")" *)
end
else backup := true;
mpars := v;
getToken; (* get "=" *)
if (ttype <> reswdtype) or (rtype <> optype) or (op <> seqop) then
begin
pp20L('Need an "=" here ',16);
errprnt;
backup := true;
end;
getToken; (* see if simple body or \...\ *)
if (ttype = delimtype) and (ch = '\') then
begin
t := nil;
repeat
getToken;
if (ttype <> delimtype) or (ch <> '\') then
begin
if t = nil then begin t := copyToken; tp := t end
else begin tp↑.next := copyToken; tp := tp↑.next end;
if ttype = identtype then (* see if it's a macro parameter *)
begin
v := mpars;
while v <> nil do (* run through parameter list *)
if v↑.name <> id then v := v↑.next (* try next *)
else
begin
tp↑.ttype := macpartype; (* yes - indicate that it is *)
tp↑.mpar := v;
v := nil;
end;
end;
end
until (ttype = delimtype) and (ch = '\');
end
else begin t := copyToken; tp := t end;
if tp <> nil then tp↑.next := nil;
macdef := t;
getToken;
end;
if (ttype = delimtype) and (ch = ',') then
begin (* set things up for another define statement *)
semiseen := true;
ttype := reswdtype;
rtype := stmnttype;
stmnt := definetype;
end;
end;
backup := true;
expandmacros := oldExpandmacros;
defineParse := b;
end;
function dimensionParse(st: statementp): boolean;
var b: boolean; v: varidefp; ndim: nodep;
function getdterm: nodep;
var n,np: nodep;
function getdfactor: nodep;
var n,np: nodep;
begin
n := newNode;
with n↑ do
begin
ntype := exprnode; (* assume expression *)
arg2 := nil;
arg3 := nil;
end;
getToken;
with curToken do
begin
if (ttype = reswdtype) and (rtype = clsetype) and
((clause = forcetype) or (clause = torquetype) or
(clause = angularvelocitytype) or (clause = velocitytype)) then
begin
ttype := identtype;
if clause = forcetype then id := forcedim↑.name
else if clause = torquetype then id := torquedim↑.name
else if clause = velocitytype then id := veldim↑.name
else id := angveldim↑.name;
end;
if (ttype = delimtype) and (ch = '(') then
begin
n↑.op := specop; (* special hack to keep parenthesis *)
n↑.arg1 := getdterm;
getDelim(')');
end
else if (ttype = reswdtype) and (rtype = optype) and (op = tinvrtop) then
begin
getDelim('(');
n↑.op := negop; (* special hack to use getdim routine *)
n↑.arg1 := getdterm;
getDelim(')');
end
else if (ttype = identtype) then
begin
n↑.ntype := leafnode;
n↑.ltype := varitype;
n↑.vari := varLookup(id);
n↑.vid := id;
if n↑.vari↑.vtype <> dimensiontype then (* no good *)
begin
pp20L('Can only have dimens',20); pp20('ion types here ',14);
errprnt;
end
end
else (* no good *)
begin
pp20L('Bad dimension expres',20); pp5('sion ',4);
errprnt;
relNode(n);
n := nil;
end
end;
getdfactor := n;
end;
begin
n := getdfactor;
getToken;
with curToken do
if (ttype = reswdtype) and (rtype = optype) and
((op = mulop) or (op = divop)) then
begin
np := newNode;
with np↑ do
begin
ntype := exprnode;
if curToken.op = mulop then op := smulop else op := sdivop;
arg1 := n;
arg2 := getdterm;
arg3 := nil;
end;
n := np;
end
else
begin
backup := true;
if (ttype <> delimtype) or ((ch <> ';') and (ch <> ')')) then
begin
pp20L('Bad dimension expres',20); pp5('sion.',5);
errprnt;
if n <> nil then relNode(n);
end;
end;
getdterm := n;
end;
begin (* dimension statement *)
b := false;
with st↑, curToken do
begin
getToken; (* get the name of the dimension type *)
if ttype <> identtype then
begin
b := true;
pp20L('Need an identifier h',20); pp5('ere. ',4);
errprnt;
end
else
begin
v := makeNewVar(dimensiontype,id);
dimname := v;
getToken; (* get "=" *)
if (ttype <> reswdtype) or (rtype <> optype) or (op <> seqop) then
begin
pp20L('Need an "=" here ',16);
errprnt;
backup := true;
end;
dimexpr := getdterm;
ndim := nil;
v↑.dim := getdim(dimexpr,ndim);
end;
end;
dimensionParse := b;
end;
function stmntParse (*: statementp *);
var badstmnt: boolean; st,sp,se: statementp;
begin
getToken; (* get first token in statement *)
with curToken do
while (ttype = delimtype) and (ch = ';') do getToken;
flushcomments := true; (* don't allow comments anywhere else *)
endOk := endOk - 1;
coendOk := coendOk - 1;
badstmnt := false; (* assume everything will be fine *)
st := newStatement;
with curToken do (* see what we've got *)
begin
if ttype = labeldeftype then
begin (* a label *)
lab↑.s := st; (* define it *)
st↑.stlab := lab; (* copy pointer to label *)
getToken; (* move on to start of statement *)
end
else st↑.stlab := nil;
semiseen := false;
if (ttype = reswdtype) and (rtype = stmnttype) then
begin
st↑.stype := stmnt;
case stmnt of
blocktype: badstmnt := blockParse(st);
coblocktype: badstmnt := coblockParse(st);
endtype,
coendtype: badstmnt := endParse(st);
iftype: badstmnt := ifParse(st);
fortype: badstmnt := forParse(st);
whiletype: badstmnt := whileParse(st);
casetype: badstmnt := caseParse(st);
returntype: badstmnt := returnParse(st);
pausetype: badstmnt := pauseParse(st);
printtype,
prompttype,
aborttype: badstmnt := printParse(st);
affixtype: badstmnt := affixParse(st);
unfixtype: badstmnt := unfixParse(st);
signaltype,
waittype: badstmnt := signalParse(st);
movetype,
opentype,
closetype,
centertype,
operatetype: badstmnt := moveParse(st);
stoptype: badstmnt := stopParse(st);
retrytype: badstmnt := retryParse(st);
cmtype: badstmnt := cmonParse(st,false);
enabletype,
disabletype: badstmnt := enableParse(st);
wristtype: badstmnt := wristParse(st);
setbasetype: badstmnt := false;
requiretype: badstmnt := requireParse(st);
definetype: badstmnt := defineParse(st);
dimdeftype: badstmnt := dimensionParse(st);
assigntype: begin (* shouldn't get here *)
badstmnt := true; (* could try to recover, but... *)
pp20L('Need a variable to a',20); pp10('ssign to. ',9); ppFlush;
errprnt;
end;
end
end
else if (ttype = reswdtype) and (rtype = filtype) and
((filler = dotype) or (filler = defertype)) then
begin
if filler = dotype then badstmnt := untilParse(st)
else
begin
st↑.stype := cmtype;
getToken;
if (ttype = reswdtype) and (rtype = stmnttype) and (stmnt = cmtype) then
badstmnt := cmonParse(st,true)
else
begin
badstmnt := true;
pp20L('Expecting an ON here',20); ppChar('.'); ppFlush;
errprnt;
end
end
end
else if (ttype = identtype) or
((ttype = reswdtype) and (rtype = optype)) then
badstmnt := assignParse(st)
else if ttype = comnttype then
begin (* comment *)
st↑.stype := commenttype;
st↑.str := str; (* copy string pointer *)
st↑.len := len;
st↑.cbody := nil;
end
else
begin (* no good - complain *)
badstmnt := true;
pp20L('Can''t start a statem',20); pp20('ent this way. ',13);
errprnt;
end;
if badstmnt then
begin
st↑.stype := emptytype; (* return empty statement *)
end;
while badstmnt do (* leave things in a "clean" state *)
begin
if (ttype = reswdtype) and
(rtype = stmnttype) and (stmnt <> assigntype) then
(* should also maybe stop when we hit a "DO", but then again maybe not *)
begin badstmnt := false; backup := true end
else if (ttype = delimtype) and (ch = ';') then badstmnt := false
else getToken; (* if still bad try next token *)
end;
end;
stmntParse := st;
end;
function eStmntParse(var cblk,newDecs: statementp; cproc: varidefp): statementp;
var s: statementp;
begin (* parse last line typed at editor *)
maxChar := eCopyLine(line);
curChar := 1;
eofError := false;
backup := false;
curToken.next := nil;
newDeclarations := nil;
curBlock := cblk;
outerBlock := cblk;
while outerBlock↑.bparent <> nil do outerBlock := outerBlock↑.bparent;
curVariable := nil;
curProc := cproc;
curMotion := nil; (* assume not *)
curCmon := nil; (* ditto *)
curErrhandler := nil; (* ditto *)
endOk := 0;
coendOk := 0;
flushcomments := true;
inCoblock := false; (* assume we're not *)
filedepth := 0;
eStmntParse := stmntParse; (* go do it *)
if newDeclarations <> nil then
begin (* set things up the way edit expects *)
s := newDeclarations↑.last;
while s↑.stype <> blocktype do s := s↑.last;
s↑.bcode := newDeclarations↑.next; (* splice new decs out *)
end; (* edit will put them back in *)
newDecs := newDeclarations
end;
(* program parser *)
function parse(fname: cstring; ppn: integer): statementp;
var s,st: statementp; fn: packed array [1..9] of char; i: integer;
begin
macrodepth := 0;
expandmacros := true;
curchar := 1;
maxchar := -1;
curline := 0;
curpage := 1;
eofError := false;
backup := false;
curToken.next := nil;
curBlock := nil;
outerBlock := nil;
curVariable := nil;
curProc := nil;
curMotion := nil;
curCmon := nil;
curErrhandler := nil;
flushcomments := true;
dimCheck := false; (* turn off dimension checking for now *)
if fname[1] = '*' then filedepth := 0 (* use tty *)
else
begin
filedepth := 1;
for i := 1 to 9 do fn[i] := upperCase(fname[i]);
reset(file1,fn,0,ppn);
getToken; (* this should flush the E directory *)
backup := true;
end;
errcount := 0;
s := newStatement;
with s↑ do
begin
stype := progtype;
pcode := stmntParse;
if pcode↑.stype <> blocktype then
begin (* make sure program enclosed in begin-end block *)
st := newStatement;
with st↑ do
begin
stype := blocktype;
bparent := nil;
blkid := nil;
variables := nil;
bcode := s↑.pcode;
appendEnd(st,bcode);
end;
pcode := st;
end;
errors := errcount;
appendEnd(s,pcode);
end;
if errcount = 0 then pp20L('No errors detected ',18)
else begin pp20L('Errors detected: ',16); ppInt(errcount) end;
ppLine;
parse := s;
end;
begin
end.